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.

SS-2012-02-09_14.42.47.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:

SS-2012-02-09_14.49.28.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.

The example is attached.

The library is available here: http://www.b4x.com/forum/additional...4834-ime-library-soft-keyboard.html#post84114
 

Attachments

  • IME.zip
    11.3 KB · Views: 7,957
Last edited:

stevel05

Expert
Licensed User
Longtime User
Have a look at FLAG_NO_SUGGESTIONS here.
 

aggelos

Member
Licensed User
Longtime User
Hello,

is there a way to know who has the focus when IME_HeightChanged fires?
i would like to scroll my scrollview to the position of that edittext


alternative.can someone please tell me how to execute

public View getCurrentFocus ()

using reflection in order to get the currently focused item?


Activity | Android Developers

Thank you
 

JesseW

Active Member
Licensed User
Longtime User
[Bug]

It was mentioned above in a post with other issues, but if the activity is minimized to show another activity, like to enter a quick text message or something, and then return to the activity with the IME, the HeightChanged event no longer fires. Here is the simplest code to demonstrate this

B4X:
#Region  Project Attributes 
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName: 
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
   #FullScreen: False
   #IncludeTitle: True
#End Region

Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim IME As IME
   Dim edt As EditText
End Sub

Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   'Activity.LoadLayout("Layout1")
   edt.Initialize("edt")
   Activity.AddView(edt, Activity.Width * 0.25, Activity.Height * 0.25, Activity.Width * 0.5, Activity.Height * 0.5)
   IME.Initialize("IME")
   IME.AddHeightChangedEvent
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub IME_HeightChanged (NewHeight As Int, OldHeight As Int)
   Msgbox(NewHeight & "," & OldHeight, "")
End Sub

before the IME activity is minimized, the msgbox will popup each time you touch the edittext or back button. switch to another activity (I use the multitask apps button to select another app) like to enter a text message, then go back and no more msgbox
 

JesseW

Active Member
Licensed User
Longtime User
[Wish] add new properties

Erel, I have coded a wrapper class for the IME object which simply passes all IME methods through to IME, and adds three new read only properties, ScreenHeight, Visible, and HardwareKeyboard. Here is the code

B4X:
'Class module

#Event: HandleAction As Boolean
#Event: HeightChanged(NewHeight As Int, OldHeight As Int)

Sub Class_Globals
   Private IME As IME
   Private mEventName As String
   Private mNewHeight As Int = -1
   Private mOldHeight As Int = -2
   Private noKbdHeight As Int = -2
End Sub

' Methods

Public Sub AddHandleActionEvent(EditText As EditText)
   IME.AddHandleActionEvent(EditText)
End Sub

'Enables the HeightChanged event.
'This event Is raised when the soft keyboard state changes.
'You can use this event to resize other views to fit the new screen size.
'Note that this event will not be raised in fullscreen activities (an Android limitation).
Public Sub AddHeightChangedEvent
   IME.AddHeightChangedEvent
End Sub

'Hides the soft keyboard if visible
Public Sub HideKeyboard
   IME.HideKeyboard
End Sub

'Initializes the object and sets the subs that will handle the events.
'Note: it is recommended that a Phone.HideKeyboard be issued prior to xIME.Initialize so this object is initialized with the soft keyboard in a known state!
Public Sub Initialize(EventName As String)
   mEventName = EventName
   IME.Initialize(mEventName)
End Sub

'Sets a custom filter.
'EditText - The target EditText.
'DefaultInputType - Sets the keyboard mode.
'AcceptedCharacters - The accepted characters.
'Example: crete a custom filter that will accept IP addresses (numbers with multiple dots).
'SetCustomFilter(EditText1, EditText1.INPUT_TYPE_NUMBERS, "0123456789.")
Public Sub SetCustomFilter(EditText As EditText, DefaultInputType As Int, AcceptedCharacters As String)
   IME.SetCustomFilter(EditText, DefaultInputType, AcceptedCharacters)
End Sub

'Sets the focus to the given view and opens the soft keyboard.
'The keyboard will only show if the view has received the focus.
'Note: [Added] The keyboard will not try to be shown if this object thinks it's shown already.
Public Sub ShowKeyboard(View As View)
   IME.ShowKeyboard(View)
End Sub

' Events

Private Sub IME_HandleAction As Boolean
   Return CallSub(Main, mEventName & "_HandleAction")
End Sub

Private Sub IME_HeightChanged(NewHeight As Int, OldHeight As Int)
   mNewHeight = NewHeight
   mOldHeight = OldHeight
   CallSub3(Main, mEventName & "_HeightChanged", NewHeight, OldHeight)
End Sub

' Properties

'[Read only] Returns the height of the usable screen.
'If no soft keyboard is present, the entire activity screen height will be returned.
'Otherwise a lesser value will be returned representing the usable height of the screen.
Public Sub getScreenHeight As Int
   Return mNewHeight
End Sub

'[Read only] Returns the visibility of the soft keyboard.
'True = Soft keyboard is visible or popped up.
'False = Soft keyboard is not vislble or closed down out of sight.
Public Sub getVisible As Boolean
   Return (mNewHeight < mOldHeight)
End Sub

'[Read only] Returns the presence of a hardware keyboard.
'True = A hardware keyboard has been found.
'False = No hardware keyboard was found.
Public Sub getHardwareKeyboard As Boolean
   Dim ref As Reflector
    ref.Target = ref.GetContext
    ref.Target = ref.RunMethod("getResources")
    ref.Target = ref.RunMethod("getConfiguration")
    Dim keyboard As Int = ref.GetField("keyboard")
    Return (keyboard <> 1) 'KEYBOARD_NOKEYS - return true if keyboard, else return false 
End Sub

the code seems to work, except for the bug report in the post above. Do you think it would be possible to incorporate this into the IME library itself?
 

JesseW

Active Member
Licensed User
Longtime User
I see this bug too. It will be checked.

Not sure about these features. They can be quite easily implemented as you did.
I'm not 100% sure getVisible is always correct. For example if the suggestions are hidden.

not saying its impossible, but in my experience, once the suggestion line comes up, it stays up. but I believe there is valid concern about the values validity after resuming a paused activity.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I checked the issue discussed above. The solution is to force the Activity to "adjustResize" mode. This is done by adding the following line to the manifest editor:
B4X:
SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)
Otherwise the OS might decide to use adjustPan mode instead.

The example in the first post was updated.
 

Jon Waterhouse

New Member
Licensed User
Longtime User
Possibly worth mentioning to avoid confusion for newbies like me...

If the Android Virtual Device you are testing on has the "Hardware keyboard present" box checked, then nothing will happen when when you click the "Show keyboard" button in the demo app.

Jon
 

tonyp

Member
Licensed User
Longtime User
In my app keys are caught via Activity_KeyPress as there is no editbox (only lists and panels), and because of that the default keyboard does not open automatically. So, I thought this IME would do the job to force the soft keyboard to display at will at the press of a button (or click on the list/panel).

(I also assume that keys entered via the soft keyboard will also fire the Activity_KeyPress event, since no other _KeyPress event is defined.)

But, I'm unable to make the soft keyboard appear. Trying this way:

B4X:
Dim kb as IME
...
kb.Initialize("Keyboard")            'in Activity.Create
...
kb.ShowKeyboard                     'in btnButton_Click event

The kb.ShowKeyboard is executed and does nothing.

Any ideas?
 
Last edited:

tonyp

Member
Licensed User
Longtime User
I was passing it Activity.

I tried your suggestion (added an editbox -- visible but hidden behind a panel, as I assume hidden wouldn't work since the object must be able to get the focus for the keyboard to show, and I don't think hidden views can get the focus, but I'm not sure -- didn't try it) and passed it to .ShowKeyboard(), and it works.

Problem now is the keys are consumed by the editbox so I now have to figure out how to deal with that via the _TextChanged event (but I think I can handle it from this point).

Thanks.
 

dlfallen

Active Member
Licensed User
Longtime User
I am making a few changes to the SQLite viewer. I am trying to have the soft keyboard popup when I make a Query Panel visible. This code is not working:
B4X:
Sub Query1_Click
Dim IME As IME
QueryPnl.Visible = True
QueryTxt.RequestFocus
IME.ShowKeyboard(QueryTxt)
End Sub

Query1 is a menu choice, and QueryTxt is an EditText view.
 

dlfallen

Active Member
Licensed User
Longtime User
I put the above code into a SUB ShowKB. This sub is in the Activity Module TableActivity, the same module as the sub Query1_Click. I tried using
B4X:
CallSubDelayed(TableActivity, "ShowKB")
but get the error message that TableActivity is an undeclared variable. I have looked for, but not found an example of using CallSubDelayed to call a sub within the same module. I tried
B4X:
CallSubDelayed(QueryPnl, "ShowKB")
and the code compiles and runs without error, but the keyboard does not pop up. I also tried
B4X:
CallSubDelayed("", "ShowKB")
but this caused B4A to crash. I'm afraid the use of CallSubDelayed is not very transparent to me.
 
Status
Not open for further replies.
Top