B4A Library [Class] View Manager

View Manager Class and 9 Patch images to put in your drawable folder (With Readonly set) that make the Edit Texts and Spinners look like Windows 3.x-Win9x Textboxes and Comboboxes. Most recent addition was the Spinner control...currently trying to get the dynamic sizing to fit the Text working on it like the Edit Text and Label use. Pretty slick little class that has Document Style Flow, Automatic handling of Edit Texts and IME for Types of data chosen, and many other features. I'll probably be adding and fixing/updating this in this post, so keep watching and make any suggestions. I'll probably be adding an Enable/Disable Method next to make use of the other 9 Patch images along with adding more control/view types. Works best in a Scrollview Panel and automatically extends height of panel given to hold controls.

9patch.zip = Images needed to Draw Controls.
ViewMgr.zip = Old/Archive version.
ViewMgr_NewCombo.zip = Latest build.

Currently needs- IME, Reflection, SQL(RestoreStateDB only, remove it to not use SQL), and StringUtils libraries

7/24/2012: Changed the way the String sizes are calculated. Still having issues with the spinner...gets size and sets padding, but has too much Top Padding. Even when setting Top to 0 the text is shoved down too far. Need some way of setting Gravity in it or something.

7/26/2012: Added Button, Add2Flow, and Enable/Disable capabilities.

7/31/2012: Fixed a Width Null Pointer Bug from a copy/paste error where textbox scale calculation used control width instead of passed width. Added -2 to -99 width to set width to percent of remaining line. Added the ability to SetText for Labels, Buttons, and Textboxes. Turned Textbox into an AutoComplete Textbox and added an Item list in the Add function for it and Combobox. Modified Label Click to store current focus so when you tap the label and its linked view already has focus it will check if it is a textbox and open the AutoComplete list (Might as well make use of the tap since keyboard won't open a 2nd time if focused anyway).

8/3/2012- Added Methods to get all and set all text using maps. Added check for Left being too small when using -1 for Top to be on same line so controls won't paint on top of each other when previous control scales and next control uses a specific value for Left.

8/7/2012- Added Sub to populate view values from a Database Cursor containing columns named the same as the view names.

8/31/2012- Enhanced Combobox to work more like it should...I hate how the Spinner works and draws. You also have the ability to make items in the list a special item with MakeComboItem that allows you to specify text to display while Getting/Setting/Storing another item/string/object.

9/7/2012- Took out some debug code that got left in last build. Tweaked Multiline Textbox Gravity and prevented Scrollviews from intercepting their scrolling (Thanks Informatix)

B4X:
'Class module
Private Sub Class_Globals
   Type MyTextbox(DataType As Int, ActionBtn As Int, ActionSub As String, ActionSubModule As Object, MinChar As Int, MaxChar As Int)
   Type MyActionSub(ActionSub As String, ActionSubModule As Object)
   Type MyCombobox(CurSelection As Int, Items As List, Prompt As String, ActionSub As String, ActionSubModule As Object)
   Type ComboItem(Text As String, Value As Object)
   Private IME As IME ' Put here instead of below so class is considered an Activity
   Private MyViews As List
   Private NameMap As Map
   Private CurFocus As View
   Public Padding As Int
   Public MinMaxWarn As Boolean
   Public ScaleViews As Boolean
   Public OverrideFontScale As Boolean
   Private FontScale As Float
   Private ScaleLabel As Label
   Private Parent As Panel
   Public ParentWidth As Int
   Private CurX As Int
   Private NextX As Int
   Private CurY As Int
   Private NextY As Int
End Sub

' Initialize the Class
' Container: Panel to place all controls in
' ShowMinMaxWarn: Set if Toast Messages are to show when Min and Max Char requirements aren't met
' ViewScaling: Sets if Views are to be scaled to fit the text they contain
' OverrideFontScaling: Set if You wish to override the user's font size scale
Public Sub Initialize(Container As Panel, ContainerWidth As Int, ShowMinMaxWarn As Boolean, ViewScaling As Boolean, OverrideFontScaling As Boolean)
Dim Ref As Reflector

   MyViews.Initialize
   NameMap.Initialize
   IME.Initialize("IME")
   ScaleLabel.Initialize("")
   ScaleLabel.Visible = False
   Container.AddView(ScaleLabel, 0, 0, 1, 1)
   Padding = 5dip
   MinMaxWarn = ShowMinMaxWarn
   ScaleViews = ViewScaling
   OverrideFontScale = OverrideFontScaling
   Parent = Container
   ParentWidth = ContainerWidth
   CurX = 5dip
   NextX = 5dip
   CurY = 5dip
   NextY = 5dip
   
   Ref.Target = Ref.GetContext
   Ref.Target = Ref.RunMethod("getResources")
   Ref.Target=Ref.RunMethod("getConfiguration")
   FontScale= Ref.GetField("fontScale")
End Sub

#Region Constants
' General Text Data [Type_Class_Text]
Sub DataType_Text As Int
   Return 1
End Sub

' Numbers Only [Type_Class_Number]
Sub DataType_Num As Int
   Return 2
End Sub

' Floating Point Numbers with sign and decimal [Type_Class_Number | Type_Number_Flag_Decimal | Type_Number_Flag_Signed]
Sub DataType_Float As Int
   Return 12290
End Sub

' DateTime Text [Type_Class_DateTime]
Sub DataType_DateTime As Int
   Return 4
End Sub

' Date Text [Type_Class_DateTime | Type_DateTime_Variation_Date]
Sub DataType_Date As Int
   Return 20
End Sub

' Time Text [Type_Class_DateTime | Type_DateTime_Variation_Time]
Sub DataType_Time As Int
   Return 36
End Sub

' Password [Type_Class_Text | Type_Text_Variation_Password]
Sub DataType_Password As Int
   Return 129
End Sub

' Email Text [Type_Class_Text | Type_Text_Variation_Email_Address]
Sub DataType_Email As Int
   Return 33
End Sub

' URL Text [Type_Class_Text | Type_Text_Variation_URI]
Sub DataType_URL As Int
   Return 17
End Sub

' Phone Number Text [Type_Class_Phone]
Sub DataType_Phone As Int
   Return 3
End Sub

' Name Text [Type_Class_Text | Type_Text_Variation_Person_Name | Type_Text_Flag_Cap_Words]
Sub DataType_Name As Int
   Return 8289
End Sub

' Uppercase Every Char [Type_Class_Text | Type_Text_Cap_Characters]
' (Overrides all other Uppercase Flags)
Sub DataType_Uppercase_All As Int
   Return 4097
End Sub

' Uppercase First Letter of Each Sentence [Type_Class_Text | Type_Text_Cap_Sentences]
Sub DataType_Uppercase_Sentences As Int
   Return 16385
End Sub

' Uppercase First Letter of Each Word [Type_Class_Text | Type_Text_Cap_Words]
' (Overrides Sentences Uppercase Flag)
Sub DataType_Uppercase_Words As Int
   Return 8193
End Sub

' Text should have Auto Correction Applied [Type_Class_Text | Type_Text_Flag_Auto_Correct]
Sub DataType_Auto_Correct As Int
   Return 32769
End Sub

' No Suggestions/Auto Correct [Type_Class_Text | Type_Text_Flag_No_Suggestions]
Sub DataType_No_Suggestions As Int
   Return 524289
End Sub

' Shows Next Button and will navigate to Next View
Sub ActionBtn_Next As Int
   Return 5
End Sub

' Shows Previous Button and will navigate to Previous View
Sub ActionBtn_Previous As Int
   Return 7
End Sub

' Shows Done Button and has option to execute ActionSub
Sub ActionBtn_Done As Int
   Return 6
End Sub

' Shows Go Button and has option to execute ActionSub
Sub ActionBtn_Go As Int
   Return 2
End Sub

' Shows Search Button and has option to execute ActionSub
Sub ActionBtn_Search As Int
   Return 3
End Sub

' Shows Send Button and has option to execute ActionSub
Sub ActionBtn_Send As Int
   Return 4
End Sub

' Upper and Lower Case Alpha Chars
' Returns: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
Sub CharFilter_Alpha As String
   Return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
End Sub

' Uppercase Alpha Chars
' Returns: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Sub CharFilter_Upper_Alpha As String
   Return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
End Sub

' Number Chars
' Returns: "0123456789"
Sub CharFilter_Numeric As String
   Return "0123456789"
End Sub
#End Region

' Add a view of your own (Not Managed by the Class) to the Flow/Layout of views managed by the class
' Left/Top: Position to place view, -1 to Place in Current Flow Position, -2 to Place in Next Flow Position.
' Width/Height: Width/Height of View. -1 to Fit to Full Width/Height. -2 to -99 for percent of width
Sub Add2Flow(NewView As View, Left As Int, Top As Int, Width As Int, Height As Int) As Rect
Dim viewRect As Rect

   If Top = -2 Then
      Top = NextY
   Else If Top = -1 Then
      Top = CurY
      If Left > 0 AND Left < NextX Then Left = NextX
   End If
   If Left = -2 Then
      Left = NextX
   Else If Left = -1 Then
      Left = CurX
   End If
   
   If Width < 1 Then
      If Width < -1 AND Width > -100 Then Width = Abs(Width) * .01 * (ParentWidth - Padding - Left) Else Width = ParentWidth - Padding - Left
   End If
   If Height < 1 Then
      If Height < -1 AND Height > -100 Then Height = Abs(Height) * .01 * (Parent.Height - Padding - Top) Else Height = Parent.Height - Padding - Top
   End If
   
   Parent.AddView(NewView, Left, Top, Width, Height)
   
   CurX = Left
   NextX = Left + NewView.Width + Padding
   CurY = Top
   NextY = Max(NextY, Top + NewView.Height + Padding)
   If NextY > Parent.Height Then Parent.Height = NextY
   
   viewRect.Initialize(Left, Top, Left + NewView.Width, Top + NewView.Height)
   Return viewRect
End Sub

' Left/Top: Position to place view, -1 to Place in Current Flow Position, -2 to Place in Next Flow Position.
' Width/Height: Width/Height of View. If ScaleViews is True it will adjust to fit font Height (Set Width= 1 to Scale width). -1 to Fit to Full Width/Height. -2 to -99 for percent of width.
' Text: Text of Label.
' Color: Color of Label
' LinkedView: Name String for View that will get focus when Label is Clicked
Sub AddLabel(Left As Int, Top As Int, Width As Int, Height As Int, Text As String, TextSize As Float, Color As Int, LinkedView As String) As Rect
Dim myControl As Label
Dim viewRect As Rect
Dim ref As Reflector

   myControl.Initialize("Label")
   myControl.Gravity = Bit.Or(Gravity.Left, Gravity.Top)
   myControl.Text = Text
   myControl.TextColor = Color
   myControl.TextSize = TextSize
   myControl.Typeface = Typeface.DEFAULT_BOLD
   ref.Target = myControl
   ref.RunMethod2("setLines", 1, "java.lang.int")
   ref.RunMethod2("setHorizontallyScrolling", True, "java.lang.boolean")
   ref.RunMethod2("setEllipsize", "MARQUEE", "android.text.TextUtils$TruncateAt")
   ref.RunMethod2("setMarqueeRepeatLimit", -1, "java.lang.int")
   ref.RunMethod2("setSelected", True, "java.lang.boolean")
   
   myControl.Tag = LinkedView
   
   If Top = -2 Then
      Top = NextY
   Else If Top = -1 Then
      Top = CurY
      If Left > 0 AND Left < NextX Then Left = NextX
   End If
   If Left = -2 Then
      Left = NextX
   Else If Left = -1 Then
      Left = CurX
   End If
   
   If Width < 1 Then
      If Width < -1 AND Width > -100 Then Width = Abs(Width) * .01 * (ParentWidth - Padding - Left) Else Width = ParentWidth - Padding - Left
   End If
   If Height < 1 Then
      If Height < -1 AND Height > -100 Then Height = Abs(Height) * .01 * (Parent.Height - Padding - Top) Else Height = Parent.Height - Padding - Top
   End If
   
   If OverrideFontScale = True Then myControl.TextSize = myControl.TextSize / FontScale
   
   If ScaleViews = True Then ' Scale View to Size required for Font
      Dim charRect As Rect
      charRect = GetCharSize(myControl.Typeface, myControl.TextSize, Text)
      
      If Height < charRect.Bottom Then Height = charRect.Bottom
      If Text.Length > 0 AND Width = 1 Then ' Calculate Min Width
         Width = Min(charRect.Right, ParentWidth - Padding - Left)
      Else
         Width = Min(Width, ParentWidth - Padding - Left)
      End If
   End If
   
   Parent.AddView(myControl, Left, Top, Width, Height)
   
   CurX = Left
   NextX = Left + myControl.Width + Padding
   CurY = Top
   NextY = Max(NextY, Top + myControl.Height + Padding)
   If NextY > Parent.Height Then Parent.Height = NextY
   
   viewRect.Initialize(Left, Top, Left + myControl.Width, Top + myControl.Height)
   Return viewRect
End Sub

Private Sub Label_Click
Dim Name As String
Dim MyControl As Label
Dim LV As View
   
   MyControl = Sender
   Name = MyControl.Tag
   If Name.Length > 0 Then
      LV = NameMap.Get(Name)
      If LV.IsInitialized = True Then
         If CurFocus = LV Then
            If LV Is AutoCompleteEditText Then
               Dim act As AutoCompleteEditText
               act = LV
               act.ShowDropDown
            End If
         Else
            LV.RequestFocus
            CurFocus = LV
         End If
      End If
   End If
End Sub

' Left/Top: Position to place view, -1 to Place in Current Flow Position, -2 to Place in Next Flow Position.
' Width/Height: Width/Height of View. If ScaleViews is True it will adjust to fit font Height (Set Width= 1 to Scale width). -1 to Fit to Full Width/Height. -2 to -99 for percent of width.
' Text: Text of Label.
' Color: Color of Label
' ActionSub: Name of Sub to call when Button Clicked
' ActionSubModule: Activity/Module that Sub is in.  (Usually [Me] can be used if Sub is in Activity Module the View resides in).
Sub AddButton(Left As Int, Top As Int, Width As Int, Height As Int, Name As String, Text As String, TextSize As Float, Color As Int, ActionSub As String, ActionSubModule As Object) As Rect
Dim myControl As Label
Dim viewRect As Rect
Dim ref As Reflector
Dim newActionSub As MyActionSub

   myControl.Initialize("Button")
   myControl.Gravity = Gravity.CENTER
   myControl.Text = Text
   myControl.TextColor = Color
   myControl.TextSize = TextSize
   myControl.Typeface = Typeface.DEFAULT_BOLD
   newActionSub.Initialize
   newActionSub.ActionSub = ActionSub
   newActionSub.ActionSubModule = ActionSubModule
   myControl.Tag = newActionSub
   
   If Top = -2 Then
      Top = NextY
   Else If Top = -1 Then
      Top = CurY
      If Left > 0 AND Left < NextX Then Left = NextX
   End If
   If Left = -2 Then
      Left = NextX
   Else If Left = -1 Then
      Left = CurX
   End If
   
   If Width < 1 Then
      If Width < -1 AND Width > -100 Then Width = Abs(Width) * .01 * (ParentWidth - Padding - Left) Else Width = ParentWidth - Padding - Left
   End If
   If Height < 1 Then
      If Height < -1 AND Height > -100 Then Height = Abs(Height) * .01 * (Parent.Height - Padding - Top) Else Height = Parent.Height - Padding - Top
   End If
   
   If OverrideFontScale = True Then myControl.TextSize = myControl.TextSize / FontScale
   
   If ScaleViews = True Then ' Scale View to Size required for Font
      Dim charRect As Rect
      charRect = GetCharSize(myControl.Typeface, myControl.TextSize, Text)
      
      If Height < charRect.Bottom + 15dip Then Height = charRect.Bottom + 15dip
      If Text.Length > 0 AND Width = 1 Then ' Calculate Min Width
         Width = Min(charRect.Right + 15dip, ParentWidth - Padding - Left)
      Else
         Width = Min(Width, ParentWidth - Padding - Left)
      End If
   End If
   
   Parent.AddView(myControl, Left, Top, Width, Height)
   myControl.Background = SetNinePatchDrawable("buttonup")
   ref.Target = myControl
   ref.SetOnTouchListener("Button_Touch")
   
   CurX = Left
   NextX = Left + myControl.Width + Padding
   CurY = Top
   NextY = Max(NextY, Top + myControl.Height + Padding)
   If NextY > Parent.Height Then Parent.Height = NextY
   If Name.Length > 0 Then NameMap.Put(Name, myControl)
   
   viewRect.Initialize(Left, Top, Left + myControl.Width, Top + myControl.Height)
   Return viewRect
End Sub

Private Sub Button_Click
Dim myControl As Label
Dim tagActionSub As MyActionSub

   myControl = Sender
   tagActionSub = myControl.Tag
   If tagActionSub.IsInitialized Then
      If tagActionSub.ActionSub.Length > 0 Then CallSubDelayed(tagActionSub.ActionSubModule, tagActionSub.ActionSub)
   End If
End Sub

Private Sub Button_Touch(viewtag As Object, action As Int, x As Float, y As Float, moveEvent As Object) As Boolean
Dim myControl As Label

   myControl = Sender
   If action = 0 Then myControl.Background = SetNinePatchDrawable("buttondown")
   If action = 1 OR x < 0 OR y < 0 OR x > myControl.Width OR y > myControl.Height Then myControl.Background = SetNinePatchDrawable("buttonup")
End Sub

' Left/Top: Position to place view, -1 to Place in Current Flow Position, -2 to Place in Next Flow Position.
' Width/Height: Width/Height of View. If ScaleViews is True it will adjust to fit font Height (Set Width= 1 to Scale width to ViewChar). -1 to Fit to Full Width/Height. -2 to -99 for percent of width.
' Name: Name given to the control for reference or use in a Select Case Block.
' ActionSub: Name of Sub to call when Item Selected- Sub YourSub(Position As Int, Value As Object) as Boolean. Return True to prevent change.
' ActionSubModule: Activity/Module that Sub is in.  (Usually [Me] can be used if Sub is in Activity Module the View resides in).
' ViewChar: Set to desired number of chars visible.
Public Sub AddComboBox(Left As Int, Top As Int, Width As Int, Height As Int, Name As String, Prompt As String, Items As List, TextSize As Float, Color As Int, SelectionChangeSub As String, SelectionChangeSubModule As Object, ViewChar As Int) As Rect
Dim myControl As Label
Dim ref As Reflector
Dim viewRect As Rect
Dim newCombobox As MyCombobox

   newCombobox.Initialize
   myControl.Initialize("Combobox")
   newCombobox.CurSelection = -1
   newCombobox.Prompt = Prompt
   newCombobox.ActionSub = SelectionChangeSub
   newCombobox.ActionSubModule = SelectionChangeSubModule
   newCombobox.Items.Initialize
   If Items.IsInitialized = True AND Items.Size > 0 Then newCombobox.Items.AddAll(Items)
   newCombobox.CurSelection = -1
   myControl.Text = Prompt
   myControl.Tag = newCombobox
   myControl.TextColor = Color
   myControl.TextSize = TextSize
   myControl.Gravity = Bit.Or(Gravity.Left, Gravity.CENTER_VERTICAL)
   ref.Target = myControl
   ref.RunMethod2("setLines", 1, "java.lang.int")
   ref.RunMethod2("setHorizontallyScrolling", True, "java.lang.boolean")
   ref.RunMethod2("setEllipsize", "MARQUEE", "android.text.TextUtils$TruncateAt")
   ref.RunMethod2("setMarqueeRepeatLimit", -1, "java.lang.int")
   ref.RunMethod2("setSelected", True, "java.lang.boolean")
   
   If Top = -2 Then
      Top = NextY
   Else If Top = -1 Then
      Top = CurY
      If Left > 0 AND Left < NextX Then Left = NextX
   End If
   If Left = -2 Then
      Left = NextX
   Else If Left = -1 Then
      Left = CurX
   End If
   
   If Width < 1 Then
      If Width < -1 AND Width > -100 Then Width = Abs(Width) * .01 * (ParentWidth - Padding - Left) Else Width = ParentWidth - Padding - Left
   End If
   If Height < 1 Then
      If Height < -1 AND Height > -100 Then Height = Abs(Height) * .01 * (Parent.Height - Padding - Top) Else Height = Parent.Height - Padding - Top
   End If
   
   If OverrideFontScale = True Then myControl.TextSize = myControl.TextSize / FontScale
   
   If ScaleViews = True Then ' Scale View to Size required for Font
      Dim charRect As Rect
      charRect = GetCharSize(Typeface.DEFAULT, myControl.TextSize, "")
      If Height < charRect.Bottom + 11dip Then Height = charRect.Bottom + 11dip
      If ViewChar > 0 AND Width < 2 Then ' Calculate Min Width
         Width = Min(charRect.Right * ViewChar + 50dip, ParentWidth - Padding - Left)
      Else
         Width = Min(Width, ParentWidth - Padding - Left)
      End If
   End If
   
   Parent.AddView(myControl, Left, Top, Width, Height)
   myControl.Background = SetNinePatchDrawable("comboboxenabled")
   ref.Target = myControl
   ref.RunMethod4("setPadding", Array As Object(6dip, 6dip, 44dip, 5dip), Array As String("java.lang.int", "java.lang.int", "java.lang.int", "java.lang.int"))
   
   CurX = Left
   NextX = Left + myControl.Width + Padding
   CurY = Top
   NextY = Max(NextY, Top + myControl.Height + Padding)
   If NextY > Parent.Height Then Parent.Height = NextY
   
   If Name.Length > 0 Then NameMap.Put(Name, myControl)
   
   viewRect.Initialize(Left, Top, Left + myControl.Width, Top + myControl.Height)
   Return viewRect
End Sub

Private Sub Combobox_Click
Dim MyLabel As Label
Dim MCB As MyCombobox
Dim MyComboItem As ComboItem
Dim items As List
Dim selection As Int

   MyLabel = Sender
   MCB = MyLabel.Tag
   If MCB.items.Size > 0 Then
      items.Initialize
      For i = 0 To MCB.items.Size - 1
         If MCB.items.Get(i) Is ComboItem Then
            MyComboItem = MCB.items.Get(i)
            items.Add(MyComboItem.Text)
         Else
            items.Add(MCB.items.Get(i))
         End If
      Next
      selection= InputList(items, MCB.Prompt, MCB.CurSelection)
      If selection < 0 Then Return
      MCB.CurSelection= selection
      If MCB.items.Get(selection) Is ComboItem Then
         MyComboItem = MCB.items.Get(selection)
         MyLabel.Text= MyComboItem.Text
         If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, selection, MyComboItem.Value)
      Else
         MyLabel.Text= MCB.items.Get(selection)
         If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, selection, MCB.items.Get(selection))
      End If
   Else
      ToastMessageShow("No Items to Select", True)
   End If
End Sub

' Create a Special Text/Value Combo to be used in a Combobox List
Public Sub MakeComboItem(Text As String, Value As Object) As ComboItem
Dim MyComboItem As ComboItem

   MyComboItem.Initialize
   MyComboItem.Text = Text
   MyComboItem.Value = Value
   
   Return MyComboItem
End Sub

' Left/Top: Position to place view, -1 to Place in Current Flow Position, -2 to Place in Next Flow Position.
' Width/Height: Width/Height of View. If ScaleViews is True it will adjust to fit font (1 for Min and 2 for Max Char Width). -1 to Fit to Full Width/Height. -2 to -99 for percent of width.
' Name: Name given to the control for reference or use in a Select Case Block.
' DataType: Use one of the Constants within this Class to Specify the Data Type for the View (Can combine with OR).
' CharFilter: Use one of the Constants within this Class and/or your own string to Specify the Text Filter for the View's Allowed Chars (Join Strings with &amp;).
' ActionBtn: Use one of the Constants within this Class to Specify the Action Button for the View's Keyboard.
' ActionSub: Name of Sub to call for Action Buttons other than Next and Previous.
' ActionSubModule: Activity/Module that Sub is in.  (Usually [Me] can be used if Sub is in Activity Module the View resides in).
' Min/Max Char: Set to desired value or 0 for no restriction.
Public Sub AddTextBox(Left As Int, Top As Int, Width As Int, Height As Int, Name As String, Text As String, TextSize As Float, Hint As String, MultiLine As Boolean, DataType As Int, CharFilter As String, ActionBtn As Int, ActionSub As String, ActionSubModule As Object, MinChar As Int, MaxChar As Int, AutoCompleteItems As List) As Rect
Dim myControl As AutoCompleteEditText
Dim ref As Reflector
Dim viewRect As Rect
Dim newTextBox As MyTextbox

   newTextBox.Initialize
   myControl.Initialize("Edit")
   newTextBox.DataType= DataType
   newTextBox.ActionBtn= ActionBtn
   newTextBox.ActionSub= ActionSub
   newTextBox.ActionSubModule= ActionSubModule
   newTextBox.MinChar= MinChar
   newTextBox.MaxChar= MaxChar
   myControl.Tag= newTextBox
   myControl.Text= Text
   myControl.Hint= Hint
   myControl.Typeface= Typeface.DEFAULT
   myControl.TextSize= TextSize
   
   If Top = -2 Then
      Top = NextY
   Else If Top = -1 Then
      Top = CurY
      If Left > 0 AND Left < NextX Then Left = NextX
   End If
   If Left = -2 Then
      Left = NextX
   Else If Left = -1 Then
      Left = CurX
   End If
   
   If Width < 1 Then
      If Width < -1 AND Width > -100 Then Width = Abs(Width) * .01 * (ParentWidth - Padding - Left) Else Width = ParentWidth - Padding - Left
   End If
   If Height < 1 Then
      If Height < -1 AND Height > -100 Then Height = Abs(Height) * .01 * (Parent.Height - Padding - Top) Else Height = Parent.Height - Padding - Top
   End If
   
   ref.Target = myControl
   If MultiLine Then
      myControl.SingleLine = False
      myControl.Wrap= True
      myControl.Gravity= Bit.Or(Gravity.Left, Gravity.Top)
      newTextBox.DataType = Bit.Or(newTextBox.DataType, 131072) ' Multiline Text Flag
      ref.RunMethod2("setImeOptions", Bit.Or(1073741824, newTextBox.ActionBtn), "java.lang.int") 'flagNoEnterAction= 1073741824
      ref.SetOnTouchListener("Edit_Touched")
   Else
      myControl.SingleLine = True
      myControl.Wrap= False
      ref.RunMethod2("setImeOptions", Bit.Or(268435456, newTextBox.ActionBtn), "java.lang.int") 'flagNoExtractUi= 268435456
   End If
   myControl.InputType = newTextBox.DataType
   
   If OverrideFontScale = True Then myControl.TextSize = myControl.TextSize / FontScale
   
   If ScaleViews = True Then ' Scale View to Size required for Font
      Dim charRect As Rect
      charRect = GetCharSize(myControl.Typeface, myControl.TextSize, "")
      If Height < charRect.Bottom + 7dip Then Height = charRect.Bottom + 7dip
      If Width < 3 Then ' Calculate Min Width
         If Width = 1 Then
            Width = Min(charRect.Right * MinChar + 7dip, ParentWidth - Padding - Left)
         Else ' Width = 2
            Width = Min(charRect.Right * MaxChar + 7dip, ParentWidth - Padding - Left)
         End If
      Else
         Width = Min(Width, ParentWidth - Padding - Left)
      End If
   End If
   
   Parent.AddView(myControl, Left, Top, Width, Height)
   myControl.Background = SetNinePatchDrawable("editenabled")
   
   CurX = Left
   NextX = Left + myControl.Width + Padding
   CurY = Top
   NextY = Max(NextY, Top + myControl.Height + Padding)
   If NextY > Parent.Height Then Parent.Height = NextY
   
   If CharFilter.Length = 0 Then
      If Bit.And(DataType, DataType_Phone) = DataType_Phone Then
         CharFilter = CharFilter_Numeric & "*#+-P()N,/ "
      Else If Bit.And(DataType, DataType_URL) = DataType_URL Then
         CharFilter = CharFilter_Alpha & CharFilter_Numeric & "$.,'-+!*_~:%()"
      Else If Bit.And(DataType, DataType_Email) = DataType_Email Then
         CharFilter = CharFilter_Alpha & CharFilter_Numeric & "!#$%&'*+-/=?^_`{|}~."
      Else If Bit.And(DataType, DataType_Float) = DataType_Float Then
         CharFilter = CharFilter_Numeric & ".-"
      Else If Bit.And(DataType, DataType_Num) = DataType_Num Then
         CharFilter = CharFilter_Numeric
      End If
   End If
   If CharFilter.Length > 0 Then IME.SetCustomFilter(myControl, myControl.InputType, CharFilter)
   
   IME.AddHandleActionEvent(myControl)
   
   If AutoCompleteItems.IsInitialized Then myControl.SetItems(AutoCompleteItems)
   MyViews.Add(myControl)
   If Name.Length > 0 Then NameMap.Put(Name, myControl)
   
   viewRect.Initialize(Left, Top, Left + myControl.Width, Top + myControl.Height)
   Return viewRect
End Sub

Private Sub IME_HandleAction As Boolean
Dim ET As View
Dim MTB As MyTextbox
Dim index As Int

   ET = Sender
   MTB = ET.Tag
   index = MyViews.IndexOf(ET)
   If index > -1 Then
      Select Case MTB.ActionBtn
         Case ActionBtn_Next
            index= index + 1
            Do While index < MyViews.Size
               ET= MyViews.Get(index)
               If ET.Enabled = True Then
                  ET.RequestFocus
                  Exit
               End If
               index= index + 1
            Loop
         Case ActionBtn_Previous
            index= index - 1
            Do While index > -1
               ET= MyViews.Get(index)
               If ET.Enabled = True Then
                  ET.RequestFocus
                  Exit
               End If
               index= index - 1
            Loop
         Case Else ' Done, Go, Search, Send
            If MTB.ActionSub.Length > 0 Then CallSubDelayed(MTB.ActionSubModule, MTB.ActionSub)
            Return False
      End Select
      Return True
   End If
End Sub

' Prevent Scrollview Scrolling for Multiline Textboxes
Private Sub Edit_Touched(viewTag As Object, action As Int, x As Float, y As Float, motionEvent As Object) As Boolean
   If action = 2 Then '= MOVE
      Dim ref As Reflector
      ref.Target = Parent
      ref.RunMethod2("requestDisallowInterceptTouchEvent", True, "java.lang.boolean")
   End If
End Sub

Private Sub Edit_FocusChanged (HasFocus As Boolean)
   If HasFocus = True Then
      'IME.ShowKeyboard(Sender)
   Else
      If MinMaxWarn = True Then
         Dim ET As EditText
         Dim MTB As MyTextbox
         Dim index As Int
         
         ET = Sender
         index = MyViews.IndexOf(ET)
         If index > -1 Then
            MTB = ET.Tag
            If ET.Text.Length < MTB.MinChar Then ToastMessageShow("Value Needs to be at least " & MTB.MinChar & " Characters Long", True)
         End If
      End If
   End If
End Sub

Private Sub Edit_TextChanged (Old As String, New As String)
Dim cursorPOS As Int
Dim ET As EditText
Dim MTB As MyTextbox

   If New.CompareTo(Old) <> 0 Then
      ET = Sender
      MTB = ET.Tag
      cursorPOS = ET.SelectionStart
      If MTB.MaxChar > 0 Then
         If New.Length > MTB.MaxChar AND MinMaxWarn = True Then ToastMessageShow("Too many Characters input", True)
         ET.Text = New.SubString2(0, Min(MTB.MaxChar, New.Length))
      End If
      ET.SelectionStart = Min(cursorPOS, ET.Text.Length)
   End If
End Sub

' Set the Items in a Combobox
Public Sub SetItems(Name As String, Items As List)
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = True AND FoundName.Tag Is MyCombobox Then
      Dim MCB As MyCombobox
      Dim MyLabel As Label
      MyLabel= FoundName
      MCB = FoundName.Tag
      MCB.Items.Clear
      MCB.CurSelection= -1
      MyLabel.Text = MCB.Prompt
      If Items.IsInitialized AND Items.Size > 0 Then MCB.Items.AddAll(Items)
   End If
End Sub

' Get Current Index of Combobox or Text Selection of a Textbox
Public Sub GetCurSelection(Name As String) As Object
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = False Then Return ""
   If FoundName Is Label Then
      If FoundName.Tag Is MyCombobox Then
         Dim MCB As MyCombobox
         MCB = FoundName.Tag
         Return MCB.CurSelection
      Else If FoundName Is AutoCompleteEditText Then
         Dim SelStart, SelEnd As Int
         Dim ref As Reflector
         Dim MyEdit As AutoCompleteEditText
         MyEdit= FoundName
         SelStart = MyEdit.SelectionStart
         ref.Target= MyEdit
         SelEnd= ref.RunMethod("getSelectionEnd")
         If SelStart = -1 OR SelEnd = -1 Then Return "" Else Return MyEdit.Text.SubString2(Min(SelStart, SelEnd), Max(SelStart, SelEnd))
      End If
   End If
End Sub

' Set Current Selection in Combobox
Public Sub SetCurSelection(Name As String, Index As Int)
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = True Then
      If FoundName.Tag Is MyCombobox Then
         Dim MCB As MyCombobox
         Dim MyLabel As Label
         MyLabel= FoundName
         MCB= FoundName.Tag
         If Index > -1 AND Index < MCB.Items.Size Then
            MCB.CurSelection= Index
            If MCB.items.Get(Index) Is ComboItem Then
               Dim MyComboItem As ComboItem
               MyComboItem = MCB.items.Get(Index)
               MyLabel.Text= MyComboItem.Text
               If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, Index, MyComboItem.Value)
            Else
               MyLabel.Text= MCB.items.Get(Index)
               If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, Index, MCB.items.Get(Index))
            End If
         Else
            MCB.CurSelection= -1
            MyLabel.Text= MCB.Prompt
            If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, -1, "")
         End If
      End If
   End If
End Sub

' Get Value of Named Views
Public Sub GetValue(Name As String) As Object
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = False Then
      Return ""
   Else
      If FoundName Is Label Then
         Dim myLabel As Label
         myLabel = FoundName
         If myLabel.Tag Is MyCombobox Then
            Dim MCB As MyCombobox
            MCB = myLabel.Tag
            If MCB.CurSelection > -1 Then
               If MCB.Items.Get(MCB.CurSelection) Is ComboItem Then
                  Dim MyComboItem As ComboItem
                  MyComboItem= MCB.Items.Get(MCB.CurSelection)
                  Return MyComboItem.Value
               Else
                  Return MCB.Items.Get(MCB.CurSelection)
               End If
            Else
               Return ""
            End If
         Else
            Return myLabel.Text
         End If
      Else
         Return ""
      End If
   End If
End Sub

' Set Text of Named Views
Public Sub SetText(Name As String, Value As Object)
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = True Then
      If FoundName Is Label Then
         Dim myLabel As Label
         myLabel = FoundName
         If myLabel.Tag Is MyCombobox Then
            Dim MCB As MyCombobox
            Dim MyComboItem As ComboItem
            MCB = myLabel.Tag
            MCB.CurSelection= -1
            For i= 0 To MCB.Items.Size - 1
               If MCB.Items.Get(i) Is ComboItem Then
                  MyComboItem= MCB.Items.Get(i)
                  If MyComboItem.Value = Value OR (IsNumber(Value) AND MyComboItem.Value = Bit.ParseInt(Value, 10)) Then
                     MCB.CurSelection= i
                     myLabel.Text= MyComboItem.Text
                     If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, i, MyComboItem.Value)
                     Exit
                  End If
               Else
                  If MCB.Items.Get(i) = Value Then
                     MCB.CurSelection = i
                     myLabel.Text = Value
                     If MCB.ActionSub.Length > 0 Then CallSubDelayed3(MCB.ActionSubModule, MCB.ActionSub, i, MCB.items.Get(i))
                     Exit
                  End If
               End If
            Next
            If MCB.CurSelection = -1 Then myLabel.Text= MCB.Prompt
         Else
            myLabel.Text = Value
         End If
      End If
   End If
End Sub

' Get Map of all Named Views Values
Public Sub SaveState As Map
Dim newMap As Map
Dim curValue As Object

   newMap.Initialize
   For i = 0 To NameMap.Size - 1
      If NameMap.GetValueAt(i) Is Label Then
         Dim myLabel As Label
         myLabel = NameMap.GetValueAt(i)
         If myLabel.Tag Is MyCombobox Then
            Dim MCB As MyCombobox
            MCB = myLabel.Tag
            curValue = MCB.CurSelection
         Else
            curValue = myLabel.Text
         End If
      Else
         curValue = ""
      End If
      newMap.Put(NameMap.GetKeyAt(i), curValue)
   Next
   Return newMap
End Sub

' Set Values of all Named Views with Provided Map
Public Sub RestoreState(Values As Map)
Dim myView As View

   For i = 0 To Values.Size - 1
      myView = NameMap.Get(Values.GetKeyAt(i))
      If myView.IsInitialized Then
         If myView Is Label Then
            Dim myLabel As Label
            myLabel = myView
            If myLabel.Tag Is MyCombobox Then
               Dim MCB As MyCombobox
               MCB = myLabel.Tag
               If Values.GetValueAt(i) Is Int AND Values.GetValueAt(i) > -1 AND Values.GetValueAt(i) < MCB.Items.Size Then
                  MCB.CurSelection= Values.GetValueAt(i)
                  If MCB.Items.Get(MCB.CurSelection) Is ComboItem Then
                     Dim MyComboItem As ComboItem
                     MyComboItem= MCB.Items.Get(MCB.CurSelection)
                     myLabel.Text= MyComboItem.Text
                  Else
                     myLabel.Text= MCB.Items.Get(MCB.CurSelection)
                  End If
               Else
                  MCB.CurSelection= -1
                  myLabel.Text = MCB.Prompt
               End If
            Else
               myLabel.Text = Values.GetValueAt(i)
            End If
         End If
      End If
   Next
End Sub

' Set Values of all Named Views with Provided Cursor Where the columns have the same names as the views
Public Sub RestoreStateDB(dbCursor As Cursor)
   If dbCursor.RowCount = 1 Then
      dbCursor.Position = 0
      For i = 0 To dbCursor.ColumnCount - 1
         SetText(dbCursor.GetColumnName(i), dbCursor.GetString2(i))
      Next
   End If
End Sub

' Get Copy of the Map of all Named Views
Public Sub GetViews As Map
Dim newMap As Map

   newMap.Initialize
   For i = 0 To NameMap.Size - 1
      newMap.Put(NameMap.GetKeyAt(i), NameMap.GetValueAt(i))
   Next
   Return newMap
End Sub

' Get the View with the given Name
Public Sub GetView(Name As String) As View
   Return NameMap.Get(Name)
End Sub

' Set Enabled status of Named View
Public Sub Enabled(Name As String, EnableView As Boolean)
Dim FoundName As View

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = True Then
      If FoundName Is EditText Then
         Dim ref As Reflector

         FoundName.Enabled = EnableView
         ref.Target = FoundName
         If EnableView = True Then
            FoundName.Background = SetNinePatchDrawable("editenabled")
            ref.RunMethod2("setFocusable", "True", "java.lang.boolean")
            ref.RunMethod2("setFocusableInTouchMode", "True", "java.lang.boolean")
         Else
            FoundName.Background = SetNinePatchDrawable("editdisabled")
            ref.RunMethod2("setFocusable", "False", "java.lang.boolean")
            ref.RunMethod2("setFocusableInTouchMode", "False", "java.lang.boolean")
         End If
      Else If FoundName Is Label Then
         FoundName.Enabled = EnableView
         If FoundName.Tag Is MyCombobox Then
            If EnableView = True Then
               FoundName.Background = SetNinePatchDrawable("comboboxenabled")
            Else
               FoundName.Background = SetNinePatchDrawable("comboboxdisabled")
            End If
         End If
      End If
   End If
End Sub

' Get the Max Width and Height of a Char or of SampleText if given
Public Sub GetCharSize(ViewTypeface As Typeface, ViewFontsize As Float, SampleText As String) As Rect
Dim testCanvas As Canvas
Dim strUtil As StringUtils
Dim testString As String
Dim CharFrame As Rect

   ScaleLabel.Typeface = ViewTypeface
   ScaleLabel.TextSize = ViewFontsize
   testCanvas.Initialize(ScaleLabel)
   CharFrame.Initialize(0,0,0,0)
   If SampleText.Length = 0 Then testString = CharFilter_Alpha & CharFilter_Numeric & "`~!@#$%^&*()_+=-[]\\{}|;':,./<>?" & QUOTE Else testString = SampleText
   ScaleLabel.Width = testCanvas.MeasureStringWidth(testString, ScaleLabel.Typeface, ScaleLabel.TextSize) + 2dip
   CharFrame.Bottom = strUtil.MeasureMultilineTextHeight(ScaleLabel, testString)
   If SampleText.Length > 0 Then
      CharFrame.Right = ScaleLabel.Width
   Else
      For x = 0 To testString.Length - 1
         CharFrame.Right = Max(CharFrame.Right, testCanvas.MeasureStringWidth(testString.CharAt(x), ScaleLabel.Typeface, ScaleLabel.TextSize))
      Next
   End If
   Return CharFrame
End Sub

Private Sub SetNinePatchDrawable(ImageName As String) As Object
    Dim r As Reflector
    Dim package As String
    Dim id As Int
    package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
    id = r.GetStaticField(package & ".R$drawable", ImageName)
    r.Target = r.GetContext
    r.Target = r.RunMethod("getResources")
    Return r.RunMethod2("getDrawable", id, "java.lang.int")
End Sub

' Test if Value in View meets Min Character Requirements
Public Sub isValid(Name As String) As Boolean
Dim FoundName As View
Dim MCB As MyCombobox

   FoundName = NameMap.Get(Name)
   If FoundName.IsInitialized = True Then
      If FoundName Is EditText Then
         Dim myEdit As EditText
         Dim MTB As MyTextbox
         
         myEdit = FoundName
         MTB = myEdit.Tag
         Return myEdit.Text.Length >= MTB.MinChar
      Else If FoundName Is Label AND FoundName.Tag Is MyCombobox Then
         MCB = FoundName.Tag
         Return MCB.CurSelection > -1
      End If
   End If
   Return False
End Sub
 

Attachments

  • 9patch.zip
    2.6 KB · Views: 474
  • ViewMgr.zip
    6.1 KB · Views: 449
  • ViewMgr_NewCombo.zip
    7.1 KB · Views: 458
Last edited:
Top