A Structure is an organized group of information that defines a specific data set. There are many types of structures available in B4X, including Classes, database records, Maps, and custom types. In this lesson we will focus mainly on the custom type.
Delphi users will recognize the custom type as a Record, and C/C++ users will recognize them as a Struct{}, and Java folks likely see them as a Data Class. In fact all of these are Class objects in each of those languages.
B4X simply allows the developer to create a custom Class to hold similar or different variable types inside it. Consider these Type definitions in B4A:
These are used in my File manager app. The first word in a custom type declaration is the keyword Type followed by the name of the class you are defining, and then all of the declarations for the variable names and types inside of that new data type or class inside the surrounding parenthesis ().
SO, cfgrec is the name of the new data type we want to create, and inside of that data type it contains 9 integers to store color values used for the different parts of the user interface. Next it stores a string that is the directory to start in each time the app runs, and then a boolean to tell the app whether or not to show hidden files.
All of that is used as a configuration record for the app and it is then stored in the apps DirInternal directory, which is protected. You MUST create a variable of the custom type before you can use that new Type:
When the app first runs, the config file is opened and read in the getcfg sub:
The sub routine above opens the confg file and reads its contents into the crec variable which is defined as our custom type cfgrec in Process_Globals. If the file does not exist or if there is another error, the catch clause of the Try/Catch executes and assigns default values to each member of out custom type and then saves that by calling the savecfg sub below.
Savecfg is called whenever something in our cfgrec custom type variable changes, like when the user selects a new color combination. Notice that we use the RandomAccessFile object to read and write the cfgrec and we only use position 0 of the file since we are only storing 1 record. Also note that cfile is not declared globally. That is because these 2 subs are the only methods that ever access the config file, and a global variable would use unneeded memory.
The code fragment below is not complete, but it is also lifted from the file manager app. It demonstrates how the elements of the Filelist type can be used. Since we declared these in the Process_Globals of the main activity, these types are available anywhere in the app. For large or complex apps, I create a code module and declare my global structures in there along with any custom sub routines to handle things globally instead, just for convenience.
In the example above, tlist is a simple list that we read a list of files into. The way the list was read (using the Mlfiles library) returns a list of strings containing information about each file separated by comas. Flist which is just before the Next in the For loop is another list, but it contains a list of our custom type named files.
Assigning to or reading from Type variables is really simple. You must specify the Type name and then the element that you want to read or change separated by a dot like this:
files.name = “somefilename.txt”
or
txt = files.name
So, files.name tells the underlying logic of B4X that we want to use the object named files and access the variable inside of it named name and assign it the string “somefilename.txt”, or read its value into the string txt.
Sorting Custom Types (Added 09-16-2015)
A very handy feature of Lists in B4X is that they support the sorting of custom types on any field. For example :
flist.SortType("date", True)
flist.SortTypeCaseInsensitive("name", True)
These two methods are built-in to Lists and make sorting a group of records easy. Both take the same arguments, the NAME of the field inside of the custom type, and whether the sort is in ascending order or not. The SortType method sorts taking capitalization into account, and the SortTypeCaseInsensitive ignores the case of each letter.
In the examples above, the first sorts the file list flist by date and the second sorts it by name (filename). We just have to tell each method the name of the field to sort by as a string or string variable. :
Sortname, sortdate, sortsize, and sorttype above are check boxes that the user can select in the display options, and sortfield is a string variable that is assigned the field name to sort by.
Summary
Custom types don't replace regular variables that you commonly use in an app, and they are not meant to. The real power of these structures is to keep information grouped into a single location that is easy to get to in your app. You can define as many Types as you need in an app. The file manager uses 6 custom Types for various purposes, but the most important is the configuration information (crec for configuration record) and all information about each file as a list of type files (held in the globally defined list flist).
In summary, You can use custom types for a whole array of things to keep information grouped into easy to access clumps of data. I find that the more specific the type is when it is defined, the better custom Types work. Custom types are very useful for reading and writing data to and from databases as well, and I think they tend to make code more readable.
This is the standard disclaimer stating that this lesson is meant to show some of the possibilities using the custom types in B4X. It is in no way the only way to use types, or the only way to achieve a given result. No animals were harmed in the writing of this lesson, but some bits may have been broken.
--- Jem
Delphi users will recognize the custom type as a Record, and C/C++ users will recognize them as a Struct{}, and Java folks likely see them as a Data Class. In fact all of these are Class objects in each of those languages.
B4X simply allows the developer to create a custom Class to hold similar or different variable types inside it. Consider these Type definitions in B4A:
B4X:
Type cfgrec(filefg, filebg, markfg, markbg, dircolor, linkcolor, markedcolor, infolblbg, infolblfg As Int, startdir As String, hidden As Boolean)
Type filelist(perms, owner, group As String, size As Long date, time, name As String)
Dim crec As cfgrec
Dim files As filelist
These are used in my File manager app. The first word in a custom type declaration is the keyword Type followed by the name of the class you are defining, and then all of the declarations for the variable names and types inside of that new data type or class inside the surrounding parenthesis ().
B4X:
Type cfgrec(filefg, filebg, markfg, markbg, dircolor, linkcolor, markedcolor, infolblbg, infolblfg As Int, startdir As String, hidden As Boolean)
SO, cfgrec is the name of the new data type we want to create, and inside of that data type it contains 9 integers to store color values used for the different parts of the user interface. Next it stores a string that is the directory to start in each time the app runs, and then a boolean to tell the app whether or not to show hidden files.
All of that is used as a configuration record for the app and it is then stored in the apps DirInternal directory, which is protected. You MUST create a variable of the custom type before you can use that new Type:
B4X:
Dim crec As cfgrec
When the app first runs, the config file is opened and read in the getcfg sub:
B4X:
Sub getcfg
Dim cfile As RandomAccessFile
cfile.Initialize(File.DirInternal, "ADT.cfg", False)
Try
crec = cfile.ReadObject(0)
Catch
crec.dircolor = Colors.Yellow
crec.filebg = Colors.Black
crec.filefg = Colors.White
crec.linkcolor = Colors.Green
crec.markbg = Colors.Yellow
crec.markedcolor = Colors.Magenta
crec.markfg = Colors.Black
crec.startdir = curdir
crec.infolblbg = Colors.White
crec.infolblfg = Colors.Black
crec.hidden = False
savecfg
End Try
cfile.Close
End Sub
The sub routine above opens the confg file and reads its contents into the crec variable which is defined as our custom type cfgrec in Process_Globals. If the file does not exist or if there is another error, the catch clause of the Try/Catch executes and assigns default values to each member of out custom type and then saves that by calling the savecfg sub below.
B4X:
Sub savecfg
Dim cfile As RandomAccessFile
cfile.Initialize(File.DirInternal, "ADT.cfg", False)
cfile.WriteObject(crec, False, 0)
cfile.Close
End Sub
Savecfg is called whenever something in our cfgrec custom type variable changes, like when the user selects a new color combination. Notice that we use the RandomAccessFile object to read and write the cfgrec and we only use position 0 of the file since we are only storing 1 record. Also note that cfile is not declared globally. That is because these 2 subs are the only methods that ever access the config file, and a global variable would use unneeded memory.
The code fragment below is not complete, but it is also lifted from the file manager app. It demonstrates how the elements of the Filelist type can be used. Since we declared these in the Process_Globals of the main activity, these types are available anywhere in the app. For large or complex apps, I create a code module and declare my global structures in there along with any custom sub routines to handle things globally instead, just for convenience.
B4X:
Dim tmp1 as String
If tlst.IsInitialized Then
For i = 0 To tlst.Size - 1
tmp1 = tlst.get(i)
files.perms = WordNum(tmp1, 1) 'WordNum and PosWord are found in the Mlstrings library
files.owner = WordNum(tmp1, 2)
files.group = WordNum(tmp1, 3)
files.size = WordNum(tmp1, 4)
files.date = WordNum(tmp1, 5)
files.time = WordNum(tmp1, 6)
tmp = WordNum(tmp1, 7)
gi = PosWrd(tmp1,7)
gi2 = tmp1.Length
st = tmp1.SubString2(gi,gi2)
files.name = st
flist.Add(files)
Next
End If
In the example above, tlist is a simple list that we read a list of files into. The way the list was read (using the Mlfiles library) returns a list of strings containing information about each file separated by comas. Flist which is just before the Next in the For loop is another list, but it contains a list of our custom type named files.
Assigning to or reading from Type variables is really simple. You must specify the Type name and then the element that you want to read or change separated by a dot like this:
files.name = “somefilename.txt”
or
txt = files.name
So, files.name tells the underlying logic of B4X that we want to use the object named files and access the variable inside of it named name and assign it the string “somefilename.txt”, or read its value into the string txt.
Sorting Custom Types (Added 09-16-2015)
A very handy feature of Lists in B4X is that they support the sorting of custom types on any field. For example :
flist.SortType("date", True)
flist.SortTypeCaseInsensitive("name", True)
These two methods are built-in to Lists and make sorting a group of records easy. Both take the same arguments, the NAME of the field inside of the custom type, and whether the sort is in ascending order or not. The SortType method sorts taking capitalization into account, and the SortTypeCaseInsensitive ignores the case of each letter.
In the examples above, the first sorts the file list flist by date and the second sorts it by name (filename). We just have to tell each method the name of the field to sort by as a string or string variable. :
B4X:
Sub sortname_CheckedChange(Checked As Boolean)
If sortname.Checked Then
sorttype.Checked = False
sortsize.Checked = False
sortdate.Checked = False
sortfield = "name"
End If
End Sub
Sub sortdate_CheckedChange(Checked As Boolean)
If sortdate.Checked Then
sortname.Checked = False
sortsize.Checked = False
sorttype.Checked = False
sortfield = "date"
End If
End Sub
Sortname, sortdate, sortsize, and sorttype above are check boxes that the user can select in the display options, and sortfield is a string variable that is assigned the field name to sort by.
B4X:
flist.SortTypeCaseInsensitive(sortfield, True)
Summary
Custom types don't replace regular variables that you commonly use in an app, and they are not meant to. The real power of these structures is to keep information grouped into a single location that is easy to get to in your app. You can define as many Types as you need in an app. The file manager uses 6 custom Types for various purposes, but the most important is the configuration information (crec for configuration record) and all information about each file as a list of type files (held in the globally defined list flist).
In summary, You can use custom types for a whole array of things to keep information grouped into easy to access clumps of data. I find that the more specific the type is when it is defined, the better custom Types work. Custom types are very useful for reading and writing data to and from databases as well, and I think they tend to make code more readable.
This is the standard disclaimer stating that this lesson is meant to show some of the possibilities using the custom types in B4X. It is in no way the only way to use types, or the only way to achieve a given result. No animals were harmed in the writing of this lesson, but some bits may have been broken.
--- Jem
Last edited: