B4A Library [Class] EditText Field Order

Here is a class that makes it easy to set the tab order for EditTexts on an activity and/or panel(s).

Set up a layout in the normal way then in Sub Globals:

B4X:
Dim FieldOrder As FOrder

And in Activity_Create:

B4X:
FieldOrder.Initialize(Array As Object(EditText1,EditText6,EditText5,EditText2,EditText3,EditText4),False)

The tab order will be the order the EditTexts are added to the array.

ForceDone is selected for the last EditText in the list unless Repeat is set to true. This is simple to change in the Class code if you want to.

It will compile directly to a usable library if you don't want to see the code as a module in the tabs.

I hope someone can make use of it.


Requires the Reflection Library.

V 1.0 attached
 

Attachments

  • FieldOrder.zip
    8.4 KB · Views: 931

stevel05

Expert
Licensed User
Longtime User
Hide Focus

There was a question recently on the forum about hiding focus once data has been entered, using this class it is a simple matter of hiding the last EditText in the list by making it's color the same as the background:

B4X:
EditText4.Color=Colors.Black

And then adding a Focus_Changed sub for the last EditText in the list to hide the keyboard:

B4X:
Sub EditText4_FocusChanged (HasFocus As Boolean)
   If HasFocus Then
      Dim Ph As Phone
      Ph.HideKeyboard(Activity)
   End If
End Sub
 
Last edited:

Mahares

Expert
Licensed User
Longtime User
@Steve: What if you have say 2 panels in one activity layout and you want to navigate in each panel's edit boxes independent of the other panel's edit text boxes. I modified the class by doing this:

B4X:
Public Sub Initialize(MyPanel As Panel, TheseFields As List,cRepeat As Boolean)

   Fields.Initialize
   For Each v As EditText In MyPanel
      Fields.Add(v)
   Next

I put the below code in the Activity_Create
B4X:
FieldOrder.Initialize(P,Array As Object(E1,E4,E2,E3),False)
   FieldOrder.Initialize(Panel1,Array As Object(EditText6,EditText8,EditText7),True)
It seems to work, but I am not convinced I was doing it correctly. I like to eventually apply it to several panels text boxes in a tabhost or AHViewpager. I need your blessings.
Thank you for a great contribution.
 

stevel05

Expert
Licensed User
Longtime User
Hi , I'm glad you're finding it useful. I would have just set up another instance of FOrder and managed it separately.

You could just re-initialize the same instance with a different list.

You can of course do it the way you have, but it would cause problems if you forget and add an object to the panel that isn't an EditText, I don't think the order will change using the For Each ... in Panel, if you change the order in the designer. I'm pretty sure they will come up in the order they were created. I think it would generally be better to work with a list you can see and check.

It doesn't matter which panel or activity the EditTexts are on, it uses the Android textview set focus functions to tell the OS where to send the focus once the current field is finished with it. So you can manage each chain with a different FOrder instance. What you can't do obviously, is have the same object in two chains (unless it the last object in both). But you could specify a different way through EditTexts based on other information/events.

So In Globals:

B4X:
Dim FieldOrder As FOrder
Dim FieldOrder2 As FOrder

Then
B4X:
FieldOrder.Initialize(Array As Object(E1,E4,E2,E3),False)
FieldOrder2.Initialize(Array As Object(EditText6,EditText8,EditText7),True)
 
Last edited:

Mahares

Expert
Licensed User
Longtime User
When I add a new textbox, I would like The FOrder will pick it up automatically. The way it is now, to add a new textbox, you have to go and change the code every time a new textbox is added or subtracted. In other words if I have:
B4X:
FieldOrder.Initialize(Array As Object(E1,E4,E2,E3),False)
and I want to add another E5, I have to change this line to:
B4X:
FieldOrder.Initialize(Array As Object(E1,E4,E2,E3,E5),False). That can
become impractical.
Thanks
 

stevel05

Expert
Licensed User
Longtime User
The only real problem I can see reading the EditTexts from the panel could cause is an error if there is an object on the panel that is not an Edittext, it might be prudent to add a test in the class to make sure the object is an EditText (is EditText) before you try to apply the focus order to it.
 

stevel05

Expert
Licensed User
Longtime User
I hadn't seen them when I created this but if you want to manage the set next Focus separately they are available in the Accessibility library.
 
Last edited:

Robert Valentino

Well-Known Member
Licensed User
Longtime User
Would be nice if it would skip fields that are disabled.

I have a toggle button that if selected enables some fields and if not they are disable.

NOW to make things work right in the toggle button routine I re-initialize the FieldOrder without those fields, but it would be nice if we could make it skip fields that are disabled instead of crashing.
 

stevel05

Expert
Licensed User
Longtime User
This is a set and leave process, there is no interaction when code is running so the only way to skip buttons when you change the coding is to re initialize it as you are doing.

My device doesn't crash if a button is subsequently disabled, it just stays on that field until manually moved.
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
Well thanks for the post. That makes things very clear.

Doesn't crash if I am in the field, but if it is the next field I am going to and ..

I just added the re-initialize to my Togglebutton routine so if you toggle it (not only are the fields disabled, the FieldOrder is changed to skip the field)
 

moster67

Expert
Licensed User
Longtime User
Awesome. Just what I needed!

Many thanks for this.
 

johnaaronrose

Active Member
Licensed User
Longtime User
Here is a class that makes it easy to set the tab order for EditTexts on an activity and/or panel(s).

Set up a layout in the normal way then in Sub Globals:

B4X:
Dim FieldOrder As FOrder

And in Activity_Create:

B4X:
FieldOrder.Initialize(Array As Object(EditText1,EditText6,EditText5,EditText2,EditText3,EditText4),False)

The tab order will be the order the EditTexts are added to the array.

ForceDone is selected for the last EditText in the list unless Repeat is set to true. This is simple to change in the Class code if you want to.

It will compile directly to a usable library if you don't want to see the code as a module in the tabs.

I hope someone can make use of it.


Requires the Reflection Library.

V 1.0 attached

I have 3 EditText fields (edtOSGBGridRef1, edtOSGBGridRef2, edtOSGBGridRef3), none of which have the property Set Done set. I've incorporated the Reflection library 2.40 & your FOrder.bas into my app. I also have validation (which works correctly) on these views' user-entered values e.g. for edtOSGBGridRef1, there is a Sub for FocusChanged calling the edtOSGBGridRef1Valid Sub:
B4X:
FieldOrder.Initialize(Array As Object(edtOSGBGridRef1, edtOSGBGridRef2, edtOSGBGridRef3), False)

Sub edtOSGBGridRef1_FocusChanged (HasFocus As Boolean)
  If Not(HasFocus) Then
    'Lost focus
    If Not(edtOSGBGridRef1Valid) Then
      IME.ShowKeyboard(edtOSGBGridRef1)
    End If
  End If
End Sub

Sub edtOSGBGridRef1Valid As Boolean
  Dim GR As String
  GR = edtOSGBGridRef1.Text.Trim
  If GR.Length <> 2 Then
    ToastMessageShow("OSGB Grid Reference Map must be 2 characters", True)
    Return False
  End If
  If Regex.IsMatch("H[L-Z]|J[L-W]|N[A-Z]|O[A-W]|S[A-Z]|T[A-W]", GR) Then
    ' Do nothing as valid OSGB Grid Ref Map
    Return True
  Else
    ToastMessageShow("OSGB Grid Reference Map not HL-HZ/JL-JW/NA-NZ/OA-OW/SA-SZ/TA-TW", True)
    ToastMessageShow("Correct OSGB Grid Reference Map", True)
    Return False
  End If
End Sub

This works as I want except for one feature! After the user enters a valid value for the last EditText view & presses the Done key, I'm not able to cause a change of focus to another view (e.g. a Button). I've already asked this in another thread but have no solution:
http://www.b4x.com/android/forum/threads/requestfocus-does-not-work.37334/#post-220095

PS I've tried using the IME library without success for this functionality, specifically using HandleAction: problem was that I was not able to incorporate validation on both Next (i.e. equivalent to Enter) key & pressing another View.
http://www.b4x.com/android/forum/threads/moving-to-another-edittext.37476/
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
A button does not normally take the focus. you can force it to if necessary but it looks quite ugly.

B4X:
Dim JO As JavaObject = Button1
    JO.RunMethod("setFocusableInTouchMode",Array As Object(True))

If you just want to hide focus after the last edit text you can create a hidden edittext. See here
 

johnaaronrose

Active Member
Licensed User
Longtime User
A button does not normally take the focus. you can force it to if necessary but it looks quite ugly.

B4X:
Dim JO As JavaObject = Button1
    JO.RunMethod("setFocusableInTouchMode",Array As Object(True))

If you just want to hide focus after the last edit text you can create a hidden edittext. See here

Thanks. The first method shows the button as a light blue when it receives a focus: a useful hint to the user that they should press it. It stays that colour after it is pressed until another view is pressed. The coding uses the EnterPressed event (as well as the FocusChanged event to do validation) for the last EditText view to request focus on the button after validation.

However, if the user returns to the first EditText, corrects its value & then presses the button (without pressing the Next key): the screen still shows the keyboard even though I have an IME.HideKeyboard command in the button's Click event (theOSGBGridRefToOSGBEastingNorthing, OSGBGridRefToOSGB36 & OSGB36ToGPS commands involve calculations which place values in other EditText views but do not cause showing of the keyboard). In the coding below all commands (including the IME.HideKeyboard command) were executed without error. Though as expected, the various IME.ShowKeyboard(edtOSGBGridRefn) commands were not executed as the values were valid. This is a mystery.
B4X:
Sub btnConvertOSGBGridRef_Click
  Log("b")
    If Not(edtOSGBGridRef1Valid) Then
        IME.ShowKeyboard(edtOSGBGridRef1)
        Return
    End If
    Log("b1")
    If Not(edtOSGBGridRef2Valid) Then
        IME.ShowKeyboard(edtOSGBGridRef2)
        Return
    End If
    Log("b2")
    If Not(edtOSGBGridRef3Valid) Then
        IME.ShowKeyboard(edtOSGBGridRef3)
        Return
    End If
    Log("b3")
    IME.HideKeyboard
    Log("b4")
    OSGBGridRefToOSGBEastingNorthing
    Log("b5")
    OSGBGridRefToOSGB36
    Log("b6")
    OSGB36ToGPS
    Log("b7")
End Sub

Due to this wrinkle (and maybe other dragons), it looks like the hidden EditText (much simpler) method is the better solution. With Android dev (even using B4A) it's easy to miss obvious solutions (such as the hidden EditText method) due to carrying too much mental baggage, especially when you're a noob with no Java knowledge.
 

stevel05

Expert
Licensed User
Longtime User
it's easy to miss obvious solutions

It certainly is, especially when you 'know' something should work, it takes a while to give up and use a second choice solution.
 

johnaaronrose

Active Member
Licensed User
Longtime User
It certainly is, especially when you 'know' something should work, it takes a while to give up and use a second choice solution.
The moving to the next EditText (including the hidden one after entering the last unhidden one) works OK when all the values entered are valid ones. However, if say the 2nd one has an invalid value entered, there is then a loop whereby the focus changes between the 2nd one & the 3rd one. This is because the validation is done when the focus is lost from the 2nd one. Only solution that I can see is to do the validation after EnterPressed event on the 2nd one. Would this interfere with the use of the FOrder class (controlling the order of entering) of the EditText views? If so, is there another way to do the validation?

Relevant code snippet:
B4X:
Sub edtOSGBGridRef2_FocusChanged (HasFocus As Boolean)
    Log("fc2")
    If Not(HasFocus) Then
        Log("fc2a")
        'Lost focus
        If Not(edtOSGBGridRef2Valid) Then
            IME.ShowKeyboard(edtOSGBGridRef2)
        End If
    End If
    Log("fc2z")
End Sub

Sub edtOSGBGridRef2Valid As Boolean
    If edtOSGBGridRef2.Text.Trim.Length < 2 OR edtOSGBGridRef2.Text.Trim.Length > 5 Then
        ToastMessageShow("OSGB Grid Reference Across must be 2 to 5 digits", True)
        ToastMessageShow("Correct OSGB Grid Reference Across", True)
        Return False
    End If
    ' Do nothing as valid OSGB Grid Ref Across
    Return True
End Sub
 

stevel05

Expert
Licensed User
Longtime User
I haven't tried that, but assuming that the focus is actually redirected where you want it, it should just carry on as normal.
 

johnaaronrose

Active Member
Licensed User
Longtime User
I haven't tried that, but assuming that the focus is actually redirected where you want it, it should just carry on as normal.
I changed the FocusChanged event to an EnterPressed one. It's still Ok for valid values. However, for an invalid value in the 2nd EditText view, it calls the EnterPressed Sub which calls the validation Sub, displays the error using ToastMessageShow but then moves the focus to the 3rd EditText view (presumably as a result of the FOrder stuff). It then refuses to allow leaving this 3rd EditText view (by pressing the 2nd EditTextview) to correct the 2nd EditText value, presumably as a result of invoking the FOrder stuff. What's required is to have a way of stopping this FOrder stuff after a validation error & then reinstituting it when the correction is made & the new value is validated as OK. Any ideas?

Even if I removed the validation after each EditText is entered and just did it (for all 3 EditText values) when the button is pressed, that would still cause the same problem. The only other solution that I can think of is to not use the FOrder class (but still use the hidden EditText view) and use the EnterPressed event for each EditText view: to proceed to the next EditText view (by means of the IME.ShowKeyboard command) if the value is valid; to display the error using ToastMessageShow & do nothing else i.e. await the user's correction followed by the user pressing the Next key. This would have the disadvantage that if the user presses another EditText then the validation would not be done.
 

stevel05

Expert
Licensed User
Longtime User
Can you post, or email me an example project so I can test it out a bit. I'll PM my email address if you want to send it off forum.
 

johnaaronrose

Active Member
Licensed User
Longtime User
Project .zip attached.
PS I'm about to butcher it as the current main layout should really be split into 4 layouts (i.e. each with its own button) with the new main layout just containing 4 buttons, since the current main layout has too many views (which would cause problems with the s/w keyboard).
 

Attachments

  • GPSOSGB.zip
    27.2 KB · Views: 282
Top