B4A Library [Library] Line Layout

I essentially turned my View Manager Class into a Relative Layout Library that had issues with B4A into an Enhanced B4A ScrollView with View Layout now. Slightly different way to initialize where you specify the size so calculations can be made. You can do what you want with the panel just as the scrollview does (I hid the old scroll functions since I had my own). You set the X/Y Padding for the Layout, Set Sizes for the Views (Remembered, so if no change when adding the next view you don't need to call it), then you add views using the add functions.

Layout flows sort of like text. When you want to place the next control on the same line call NextPos, and when you want a New Line call NextLine. It calculates the Next Positions automatically when you add views with the add functions. The panel automatically expands vertically and it has State Management (SaveState and RestoreState) for all views you give names to. Labels also can link to other views with names and Textboxes/EditTexts can navigate with Next/Prev buttons to other named views too.

02/06/2013: After completely converting my main app from using my old View Manager Class to this the final APK file is over 125KB smaller. This replaced the View Manager Class, the IME library, and a panel on each screen that the View Manager used. This does so much more than my class was able to do as well.

B4X:
LL.Initialize(Activity.Height, Activity.Width, "LL")
LL.SetTheme(LL.Theme_HoloLight) ' Sets to Light theme if Holo Not Available
Activity.AddView(LL, 0, 0, Activity.Width, Activity.Height)
LL.SetPadding(4dip, 8dip)

LL.SetSize(LL.Size_WrapContent, LL.Size_WrapContent)
LL.AddLabel("", "", "Type:", 18, True, 1, Colors.Red, "Type", 0)
LL.NextPos
LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
LL.AddCombobox("Type", "", "Select Party Type", "Party Type", 18, Colors.Black, True, Array As String("Suspect", "Victim", "Witness", "Prisoner"), Null, LL.GetBackgroundResourceID("comboboxenabled"))
LL.NextLine

LL.SetSize(LL.Size_WrapContent, LL.Size_WrapContent)
LL.AddLabel("", "", "First Name:", 18, True, 1, Colors.Black, "FirstName", 0)
LL.NextPos
LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
LL.AddTextbox("FirstName", "", LL.DataType_Uppercase_Words, "", LL.ActionBtn_Next, 0, 30, 1, "", 18, Colors.Black, "First Name", Colors.Gray, Null, LL.GetBackgroundResourceID("editenabled"), "MiddleName", "")
LL.NextLine

LL.SetSize(LL.Size_WrapContent, LL.Size_WrapContent)
LL.AddLabel("", "", "Middle Name:", 18, True, 1, Colors.Black, "MiddleName", 0)
LL.NextPos
LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
LL.AddTextbox("MiddleName", "", LL.DataType_Uppercase_Words, "", LL.ActionBtn_Next, 0, 30, 1, "", 18, Colors.Black, "Middle Name", Colors.Gray, Null, LL.GetBackgroundResourceID("editenabled"), "LastName", "")
LL.NextLine

LL.SetSize(LL.Size_WrapContent, LL.Size_WrapContent)
LL.AddLabel("", "", "Last Name:", 18, True, 1, Colors.Black, "LastName", 0)
LL.NextPos
LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
LL.AddTextbox("LastName", "", LL.DataType_Uppercase_Words, "", LL.ActionBtn_Next, 0, 30, 1, "", 18, Colors.Black, "Last Name", Colors.Gray, Null, LL.GetBackgroundResourceID("editenabled"), "Alias", "")
LL.NextLine

LL.SetSize(LL.Size_WrapContent, LL.Size_WrapContent)
LL.AddLabel("", "", "Alias:", 18, True, 1, Colors.Black, "Alias", 0)
LL.NextPos
LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
LL.AddTextbox("Alias", "", LL.DataType_Uppercase_Words, "", LL.ActionBtn_Next, 0, 30, 1, "", 18, Colors.Black, "Alias", Colors.Gray, Null, LL.GetBackgroundResourceID("editenabled"), "", "")
LL.NextLine

9 Patch images are in my View Manager Thread- http://www.b4x.com/forum/additional-libraries-classes-official-updates/19687-class-view-manager.html
 

Attachments

  • LineLayout.zip
    19.9 KB · Views: 399
  • LineLayout1_5.zip
    27 KB · Views: 282
  • LineLayout1_55.zip
    26 KB · Views: 292
  • LineLayout1_60.zip
    27.4 KB · Views: 459
Last edited:

grant1842

Active Member
Licensed User
Longtime User
Thanks for sharing this :)
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
Thanks for sharing this :)

You're welcome. For the 4 that got it so far after this morning's changes I just updated some of the internal event names (B4A appears to raise internal events with lowercase names, while showing the user the Mixed case versions, and I keep forgetting and had the Action Button Event and the IME Height Change event in mixed case).

I'm still seeing some weird behavior in Android that isn't just with this Library as the IME library does it too with regular scrollviews. It does it even when you add [SetActivityAttribute(main, android:windowSoftInputMode, "stateHidden|adjustResize")] to the manifest it still seems to Pan the screen and not allow for shrinking the scrollview and scrolling the full range (Top is always chopped off). The IME Height Change event (As well as the one in this since it is a copy of it) notes the Height Change isn't fired in Full Screen, but that appears to mean Landscape (With or without the fullscreen keyboard). It also appears to ignore Pan/Resize rules in Landscape since I've tried just matching the Scrollview/LineLayout to the size of the parent and no processing of IME events and it still sizes wrong.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Longtime User
Just added some requestDisallowInterceptTouchEvent tweaks- Since this is a Scrollview base class which captures Move events to scroll it interferes with some views that also scroll placed in it. I already had a Touch event handler for multiline Textboxes added with AddTextbox that sent requestDisallowInterceptTouchEvent to the parent to allow them to scroll without the parent grabbing the move events. Buttons added with AddButton containing custom Up/Down resource images also used this otherwise the parent grabbing them caused it not to detect when your finger moved out of the button region.

I modified AddView to detect Scrollview/HorizontalScrollView, ListView, and WebView to add the same Touch processing to them and allow them to work.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Longtime User
When I made the last tweaks to SetValue to make it set by index or text for Spinners/Comboboxes I had a dumb == compare that snuck in and since Strings are objects that didn't work. I replaced it with a CompareTo/contentEquals and it is working better now. Still can't get Spinners/Comboboxes to report their height right, so I've been hard coding the height as a Dip value...not ideal since Text can scale, but the way B4A sets the text size and the control itself not having text yet makes it difficult.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Longtime User
Added Version 1.5 to the first post. I created my own custom Combobox view along with methods for it and modified some existing methods that took the string name that you give a view to take the actual view object now. There is still the GetView method you can pass the name to and can get the view to use in the other functions. This allowed for easier coding in other methods since all they did was call GetView all the time too. It also allows you to pass your own views to some functions if they are compatible.

The Combo can be populated from a List/String Array or a Cursor that was populated from SQL with the first column as a int/long(64bit max) and the 2nd column as the label to show the user. It has a boolean parameter to select if the Restore that comes from a Database Cursor is an Index or text value too, so you can store int indexes in your database into a static list given to the combo to be more efficient.

I just finished it up Yesterday and tested probably 70-80% of the new functionality.

A small example showing general usage and the new Combobox:

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.Color = Colors.RGB(255, 222, 173) ' Navajo White
    Activity.Title = "Example"
    LL.Initialize(Activity.Height, Activity.Width, "")
    Activity.AddView(LL, 0, 0, -1, -1)
    LL.SetPadding(3dip, 3dip)
    LL.SetSize(LL.Size_MatchParent, LL.Size_WrapContent)
    LL.SetTheme(LL.Theme_HoloLight)
    LL.AddCombobox("", "", "Select a State", "State List", 40, Colors.Blue, False, File.ReadList(File.DirAssets, "states.txt"), Null, LL.GetBackgroundResourceID("comboboxenabled"))
    LL.NextLine
    LL.AddCombobox("", "", "Select a Value", "1-10", 40, Colors.Blue, False, Array As String("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), Null, 0)
    LL.NextLine
    LL.AddCombobox("", "", "Select a Value", "Nothing", 40, Colors.Blue, False, Null, Null, 0)
End Sub

The states.txt is just a file with each US State on its own line. The resource on that same line of code is a 9 Patch combobox image you can find in my other View Manager Class thread.

For those that develop libraries- In the custom TextView I use for the new control I call super(ba.context, null, 16842881); on initialize/construction. 16842881 is the style spinners use, so it draws it like a spinner and makes it behave like a spinner, but properly returns the height now. Pretty slick little workaround. It doesn't seem to draw in Holo style though which I thought it would. If anyone knows of an improvement, let me know.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Longtime User
Added 1.55 to First post. After finally using the Cursor to populate the Combobox in my main app I discovered it wasn't the best way of going about it. It turns out that the cursor requires a column named _id which beginners may not understand which really relates to Listviews and my implementation of it didn't care. Then I had to create a Global Cursor in my app which I didn't/couldn't close to pass to the Combobox which wasn't the best.

So...initially in 1.5 internal to the Combo I had a List and a Cursor, and whichever was Null wasn't used to fill the Popup List. Now It is only a MatrixCursor which is created with just Two Columns- "_id", and "Label". It is filled using the provided List or Cursor. I set _id to null when filled from a List to tell the difference in the Get/Set Selection functions for using the Combo Index or the Index from the Cursor that populated the combo. You can still get the List of the Combo, but it is generated from the MatrixCursor now and modifying it does nothing to the Combo. Getting the Cursor of the Combo does get the real MatrixCursor as a Cursor, but B4A doesn't have libraries for MatrixCursor, so you'd have to populate it from SQL to modify it.

I forgot to mention something else I did in 1.5 that is still in this- I moved my Disallow Intercept of Touch Events code out from AddView to its own function. I had some issues when using a Listview in a View Pager in my LineLayout and it was easier to have a function to determine which blocked the Parent from capturing Touch than have AddView make them all. Multiline Textboxes still have it added automatically. It takes a View as a parameter too as with the other 1.5 changes, so you can use it for other views too.
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
1.60 in first post:

1. Added a Long Click event for the Combobox since it/Spinners don't have them in B4A. Access it through the LineLayout object when you type Sub+Space+Tab. If you haven't used the events in this yet, the first 3 fire to the EventName given to the controls they pertain to- First two are for Combos, the imeAction fires for Textboxes/EditTexts with Action Button set to something other than Next/Previous. The imeHeightChanged you specify in the HandleKeyboardHeightChange method. The last scroll event just comes with the Scrollview and goes to the EventName you pass when initializing the LineLayout.

2. Added the ability of setting the Ems Size of Label, Button, and Combobox. You use it by setting a Width < -2 in SetSize and it sets the Ems to the Abs(Width). Textbox already had this. Ems is supposed to be the width of the widest char (Usually an M), so you set the size of the control to Character Widths.

In reality Android doesn't calculate this the best though-
A. Android internally only keeps track of a Min/Max width and when you set MinEms or MaxEms it sets these and sets the mode of sizing to Ems so it knows that the size isn't in pixels, but chars. In SetEms it just sets Min and Max to the same value and sets mode to Ems.

B. If you look at the code for onMeasure you will see Android doesn't even calculate character width, but instead multiplies your value by LineHeight. Kind of lazy in my opinion, but I guess it was faster. This will usually make your Ems value a little bigger though. Setting width to -4 for instance will SetEms to 4 in the library, but you will get a Label about 5 'M' wide.

While not accurate on Android's part it still scales for font size, so your views will keep the right width proportional to font size. I had originally opened up the CurrentX and CurrentY values of LineLayout to allow for positioning and lining up fields (or you can set a fixed Width value), but that sets a static value and this works much better since it scales.
 
Top