Android Tutorial Handle the soft keyboard with the IME library

Status
Not open for further replies.
Android has very good support for custom input method editors (IMEs).
The downside for this powerful feature is that interacting with the soft keyboard can be sometimes quite complicated.

This library includes several utilities that will help you better handle the soft keyboard.

The attached example demonstrates the available methods.

1724752241705.png


Note that the IME object should be initialized before it can be used.

Handling the screen size changed event
When the keyboard opens the available screen size becomes much shorter. By default if the EditText is located near the bottom of the screen, Android will "push" the whole activity and make the EditText visible. This mode is named "adjustPan" mode.

By calling IME.AddHeightChangedEvent you are changing the activity to "adjustSize" mode. In this mode the activity will not be pushed automatically. Instead the HeightChanged event will be raised when the keyboard is shown or hidden.

Update: You should explicitly set the adjustSize mode with the manifest editor. This is done by adding the following manifest editor code (for each activity):
B4X:
SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)

For example the following code makes sure that the button at the bottom is always visible and sets the large EditText height to match the available height:
B4X:
Sub IME_HeightChanged(NewHeight As Int, OldHeight As Int)
   btnHideKeyboard.Top = NewHeight - btnHideKeyboard.Height
   EditText1.Height = btnHideKeyboard.Top - EditText1.Top
End Sub

The result is:

1724752272411.png


Note that this method will not work if the activity is in full screen mode (Issue 5497 - android - adjustResize windowSoftInputMode breaks when activity is fullscreen - Android).

Showing and hiding the keyboard
IME.ShowKeyboard - Sets the focus to the given view and opens the soft keyboard.
IME.HideKeyboard - Hides the keyboard (this method is the same as Phone.HideKeyboard).

Handle the action button
By calling IME.AddHandleActionEvent you can override the default behavior of the action button (the button that shows Next or Done).
This event is similar to EditText_EnterPressed event. However it is more powerful. It also allows you to handle the Next button and also to consume the message (and keep the keyboard open and the focus on the current EditText).

This can be useful in several cases.
For example in a chat application you can send the message when the user presses on the done button and keep the keyboard open by consuming the message.

You can also use it to validate the input before jumping to the next view by pressing on the Next button (note that the user will still be able to manually move to the next field).

You can use the Sender keyword to get the EditText that raised the event.
For example:
B4X:
Sub IME_HandleAction As Boolean
   Dim e As EditText
   e = Sender
   If e.Text.StartsWith("a") = False Then
      ToastMessageShow("Text must start with 'a'.", True)
      'Consume the event.
      'The keyboard will not be closed
      Return True
   Else
      Return False 'will close the keyboard
   End If
End Sub

Custom filters
EditText.InputType allows you to set the keyboard mode and the allowed input.
However there are situations where you need to use a custom filter. For example if you want to accept IP addresses (ex: 192.168.0.1). In this case none of the built-in types will work. Setting the input type to INPUT_TYPE_DECIMAL_NUMBERS will get you close but it will not allow the user to write more than a single dot.
IME.SetCustomFilter allows you to both set the keyboard mode and also to set the accepted characters.
In this case we will need a code such as:
B4X:
IME.SetCustomFilter(EditText3, EditText3.INPUT_TYPE_NUMBERS, "0123456789.")
Note that this is only a simple filter. It will accept the following input (which is not a valid IP address):
....9999.

Updates:

The example was updated and is now based on B4XPages. Note that additional code in the Main module (https://www.b4x.com/android/forum/t...or-managing-multiple-pages.118901/post-745090) and don't miss the manifest editor code.
 

Attachments

  • IME_Example.zip
    14.8 KB · Views: 182
Last edited:

mmieher

Active Member
Licensed User
Longtime User
I must be stupid, but when I go to the link to download the library, it takes me to a thread that point me back here.

Where is the library?
 

Douglas Farias

Expert
Licensed User
Longtime User
hi @Erel , only to report
i dont know if this is a bug on B4A 6 or on the IME lib.
i m getting this error when i try use the ime lib, it bug on my old apps with IME. (i think the problem its the b4a 6+)


here is the bug
** Activity (main) Pause, UserClosed = false **
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Error occurred on line: 151 (Main)
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4277)
at android.view.ViewGroup.addView(ViewGroup.java:4127)
at android.view.ViewGroup.addView(ViewGroup.java:4068)
at android.view.ViewGroup.addView(ViewGroup.java:4041)
at anywheresoftware.b4a.objects.IME.AddHeightChangedEvent(IME.java:119)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:753)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:343)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
at leitor.df.texto.main.afterFirstLayout(main.java:102)
at leitor.df.texto.main.access$000(main.java:17)
at leitor.df.texto.main$WaitForLayout.run(main.java:80)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6946)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
** Activity (main) Resume **

B4X:
    Try
        IME.Initialize("IME")
        IME.AddHeightChangedEvent 'CRASH HERE
        IME_HeightChanged(100%y, 0)
        IME.AddHandleActionEvent(edtexto)
    Catch
        If LastException.IsInitialized Then Log(LastException)
    End Try

this code is under activity create (used many times with no crash on my old apps)
if i use try catch my layout is not loaded, i only can see a white screen.

Samsung J2 (Android 5.1.1)

thx
 
Last edited:

panagiotisden2

Active Member
Licensed User
Longtime User
im trying to resize a webview to show a text field inside it while the keyboard is open but this code does not work.
B4X:
Sub IME_HeightChanged(NewHeight As Int, OldHeight As Int)
    WebView1.Height=WebView1.Top-NewHeight
  
End Sub

also noticed that this code returns nothing to the logs
B4X:
Sub IME_HeightChanged(NewHeight As Int, OldHeight As Int)
    Log(NewHeight)
    Log(OldHeight)
  
End Sub

to avoid an obvious suggestion i have already added this to activity_create
B4X:
ime.Initialize(Me)
ime.AddHeightChangedEvent

edit:
and i have already added this in manifest editor
B4X:
SetActivityAttribute(Browser, android:windowSoftInputMode, adjustResize|stateHidden)
 

TheJinJ

Active Member
Licensed User
Longtime User
Is the IME library compatible with Appcompat? When I try and implement it in a project I get error:
B4X:
Error occurred on line: 120 (Main)
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    at android.view.ViewGroup.addViewInner(ViewGroup.java:4654)
    at android.view.ViewGroup.addView(ViewGroup.java:4490)
    at android.view.ViewGroup.addView(ViewGroup.java:4431)
    at android.view.ViewGroup.addView(ViewGroup.java:4404)
    at anywheresoftware.b4a.objects.IME.AddHeightChangedEvent(IME.java:119)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:753)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:343)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
    at b4a.example.main.afterFirstLayout(main.java:102)
    at b4a.example.main.access(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:80)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7229)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
** Activity (main) Resume **
WakeLock already held.

Line 119 and 120
B4X:
IME.Initialize("IME")
IME.AddHeightChangedEvent

manifest has
B4X:
SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)

If I create a blank project targeting v25 it works ok without AppCompat......
 

TheJinJ

Active Member
Licensed User
Longtime User
Have you tested my example? Does it work for you?

Yes your example works, I have found out it occurs if you use the NavigationBar from the DesignSupport Library

B4X:
    NavDrawer.Initialize2("NavDrawer", Activity, NavDrawer.DefaultDrawerWidth, NavDrawer.GRAVITY_START)
    Activity.LoadLayout("main")
 
    ime.Initialize("ime")
    ime.AddHeightChangedEvent

If the drawer isn't initialised it works ok :(
 

corwin42

Expert
Licensed User
Longtime User
I can't call it before initialized and navdrawer needs to be initialized before a layout is loaded....If navdrawer isn't initialized it will work ok
Have you tried it? I haven't tried it but it may be possible to call
ime.AddHeightChangedEvent before initializing the NavDrawer.
 

VictorTandil

Member
Licensed User
Longtime User
Hi. I have a problem that I can not solve. I have placed several Labels in a Scrollview, until then everything is fine, but when I open the keyboard to write a new one, the previous ones present strange errors.

I'm using the IME library to control the size change of the Scrollview as shown in the example, although I do not understand how the Labels can do that?

Screenshot_2018-04-24-15-50-27.png Screenshot_2018-04-24-15-50-01.png

It seems that the text comes out of the Label box and the right and bottom edges were cut.
 
Status
Not open for further replies.
Top