1 Comment

Seamless Integration of Android’s Soft Keyboard

Recently, I was working on a feature for an application where the user would interact with Android’s soft keyboard. I found working with this keyboard to be more confusing than I would have expected for such a ubiquitous feature. Baked into most Android smartphones, it is highly customizable, but at the cost of making some basic configuration details hard to figure out.

Below, I’ll compile my findings from many StackOverflow articles, Android documentation pages, and hours spent experimenting. Hopefully, I can save you some time doing your own research.

Throughout this post, you’ll see several code examples. In the interest of adaptability, I have set them up in a simple, empty single-activity Android project written in Java. These examples should apply to any single activity, but they may have different behavior across activities. Below is the base layout XML that I will be using.


<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/content_panel"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >

    <EditText
      android:id="@+id/edit_text_1"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:inputType="text" />

    <TextView
      android:id="@+id/text_view_1"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="30dp"
      android:text="Default text" />

</LinearLayout>

The Action Button

One component you will likely want to customize is the keyboard’s action button. This button is the key that you might consider the “enter” or “return” key on a typical hardware keyboard. It can be customized to display as a search, send, or many other built-in icons.

Below, you’ll see a selection of examples with the associated android:imeOptions value. Other possible values for this property can be found in the Android documentation.

When using applications, I often find that the intent of the keyboard’s action button is ambiguous. When I click it, is the keyboard going to close and submit my input? Or will it advance to the next input field? Maybe it will close the keyboard, and I’ll have to instigate the next action? Some thoughtful consideration into the styling of your keyboard’s action button can help to clarify this for your users.

Action Button Actions

Once you’ve customized your action button, you may be interested in handling the user’s input when the user presses the button. You can do this by implementing an OnEditorActionListener on the EditText element.

In the below example, I set a text view’s text to the value of the text field when the action button is pressed. I put the following code in the onCreate method of my MainActivity.java, but you could also put this inside a fragment or similar object depending on the architecture of your app and the scope of the interaction.


EditText editText = this.findViewById(R.id.edit_text_1);
final TextView textView = this.findViewById(R.id.text_view_1);

editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
  @Override
  public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
    if(view.getId() == R.id.edit_text_1 && actionId != 0) {
      textView.setText(view.getText().toString());
    }
    return false;
  }
});

In this example, we assign the edit text and text view elements to variables in the first two lines. The text view’s variable must be declared as final so that we can reference it within an inner class later. Then we call the setOnEditorActionListener method on the edit text element.

In the overridden onEditorAction method, we have a sanity check on the ID of our edit text field before checking the action ID. This action ID check is necessary if you have customized the action button. Otherwise, you will get two callbacks fired here; one for the action button and one for an “enter” event (i.e. action ID 0). Within this check, we can set the text of our text view.

Finally, we must return a Boolean indicating whether or not we have consumed the event. You will almost always want to return false here so that the keyboard can continue handling the event.

Closing the Keyboard

One interaction that I was shocked to find is not out-of-the-box for the soft keyboard is closing the keyboard when the user clicks away from EditText. This is the case because most elements are not inherently focusable.

In our simple example, once the EditText gets focus, it’s not going to lose it. To fix this, I made my root element clickable and focusable using the android:clickable, android:focusable, and android:focusableInTouchMode properties.

Setting android:clickable to true makes the root layout respond to touch/click events. When android:focusable=true and android:focusableInTouchMode=true, the layout can take focus when it is clicked. These properties do not necessarily need to be set on the root element, but keep in mind that only the screen area covered by the element they are set on will be able to close the keyboard.

Unfortunately, just setting these properties still isn’t enough; we also need to say what happens when the text field loses focus. This code can be placed in the same spot as the previous example.


EditText editText = this.findViewById(R.id.edit_text_1);

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
  @Override
  public void onFocusChange(View view, boolean hasFocus) {
    if(view.getId() == R.id.edit_text_1 && !view.hasFocus()) {
      InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
      imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
  }
});

In this code, first we assign the EditText element to a variable. Then, we implement an OnFocusChangedListener. Within the overridden onFocusChange method, we first check that the edit text element is losing focus, and if so, we close the keyboard.

The first line inside the conditional gets a reference to the system’s input method manager. The second line inside the conditional tells the system to close the keyboard. I think much of the confusion around this topic comes from these two lines; specifically since the word “keyboard” is never used. I often see these two lines refactored into the main activity as a closeKeyboard method, which might be a good idea if your app is going to use the soft keyboard frequently.

The above approach will work perfectly when you have just one edit text element, but if you have something more like a form with multiple text fields, you will notice strange behavior when changing focus from one to another. In this case, you would need to set the OnFocusChangeListener for each.