Saturday, March 23, 2013

Show / Hide Android Soft Keyboard

Here is a helpful utility class for showing and hiding the Android soft keyboard. The code is written in C# using Mono for Android but should be easy to convert to Java. Similar behavior can be accomplished using Window.SetSoftInputMode but unfortunately SetSoftInputMode doesn't always play well with a tab / fragment architecture.

This code is my attempt to simplify the process of manually showing and hiding of the soft keyboard, which is much more complicated than it has a right to be (I blame vague documentation). My earlier attempts to accomplish this were laden with frustrating bugs and strange side-effects. Now, (hopefully) all those bugs and side-effects have been corrected.

By default there is a 200 millisecond delay on the ShowSoftKeyboard method; this is to accommodate any fragment transition animations that may occur. Showing the keyboard while running an animation can result in poor performance so the workaround is to wait until the animation is finished. If you are not using fragment transition animations then the delay can be eliminated.

*Code has been tested on a handful of real devices as well as a wide range of emulator versions (2.2+).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Views.InputMethods;
using Android.InputMethodServices;
using Android.Util;

namespace Mobile.Util
{
    public static class ActivityExtensions
    {
        public static void HideSoftKeyboard(this Activity activity)
        {
            new Handler().Post(delegate
            {
                var view = activity.CurrentFocus;
                if (view != null)
                {
                    InputMethodManager manager = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
                    manager.HideSoftInputFromWindow(view.WindowToken, 0);
                }
            });
        }

        public static void ShowSoftKeyboard(this Activity activity, View view = null, int delay = 200)
        {
            new Handler().PostDelayed(delegate
            {
                view = view ?? activity.CurrentFocus;
                if (view != null)
                {
                    if (view.HasFocus)
                        view.ClearFocus(); //bug fix for older versions of android

                    view.RequestFocus();
                    InputMethodManager manager = (InputMethodManager)activity.GetSystemService(Context.InputMethodService);
                    manager.ShowSoftInput(view, 0);
                }
            }, delay);
        }
    }

    public static class DialogExtensions
    {
        public static void HideSoftKeyboard(this Dialog dialog)
        {
            new Handler().Post(delegate
            {
                var view = dialog.CurrentFocus;
                if (view != null)
                {
                    InputMethodManager manager = (InputMethodManager)dialog.Context.GetSystemService(Context.InputMethodService);
                    manager.HideSoftInputFromWindow(view.WindowToken, 0);
                }
            });
        }

        public static void ShowSoftKeyboard(this Dialog dialog, View view = null, int delay = 200)
        {
            new Handler().PostDelayed(delegate
            {
                view = view ?? dialog.CurrentFocus;
                if (view != null)
                {
                    if (view.HasFocus)
                        view.ClearFocus(); //bug fix for older versions of android

                    view.RequestFocus();
                    InputMethodManager manager = (InputMethodManager)dialog.Context.GetSystemService(Context.InputMethodService);
                    manager.ShowSoftInput(view, 0);
                }
            }, delay);
        }     
    }
}

7 comments:

  1. Hi Craig,

    I've been pulling my hair out getting the keyboard to popup on a dialog in Mono For Android.

    This is the code I have at the moment:


    var myDialog = builder.Create();
    myDialog.Show();

    InputMethodManager manager = (InputMethodManager)myDialog.Context.GetSystemService(Context.InputMethodService);
    manager.ShowSoftInput(editText, 0); // editText is the only control on the dialog, an EditText control :)

    I have also tried the delay you have in your example.

    Any idea what I might be doing wrong ?

    ReplyDelete
  2. Your code looks more or less correct I think. It might be related to the dialog's lifecycle. You might have to wait a little longer until dialog is fully created before you try to force the keyboard to show, although I would have thought the delay would have fixed it.

    I'm updating the post with my DialogExtensions class which is mostly untested.

    But... in your specific example I think you might be able to get by with just this:

    var myDialog = builder.Create();
    myDialog.Window.SetSoftInputMode(SoftInput.StateAlwaysVisible | SoftInput.AdjustResize);
    myDialog.Show();

    ReplyDelete
  3. Hi Craig,

    Thanks for replying, but it still doesn't work. I get no Keyboard :(

    These lines work for you?

    ReplyDelete
  4. I was a bit to fast, it works on the device, but not on the emulator, which is fine for me :)

    ReplyDelete
  5. Hi Craig, I'm afraid I wasn't able to get this to work in my Android project.

    My device is running 2.3.3, and after defining the class and methods as above in my (Xamarin) project, I placed a call to ActivityExtensions.HideSoftKeyboard() from my MainActivity, yet no change - the soft keyboard still makes an appearance on activity load.

    This is in the onCreate() method, right after the SetContentView() call.

    Any ideas?

    ReplyDelete
  6. Try calling Activity.Window.SetSoftInputMode method in OnCreate.

    http://developer.android.com/reference/android/view/Window.html#setSoftInputMode(int)

    See if that helps at all.

    ReplyDelete
  7. private void ShowSoftKeyboard(View input, bool selectText)
    {
    if (selectText) ((EditText)input).SelectAll();
    ThreadPool.QueueUserWorkItem(s =>{
    Thread.Sleep(100);
    RunOnUiThread(() => ((Android.Views.InputMethods.InputMethodManager)GetSystemService(InputMethodService))
    .ShowSoftInput(input, Android.Views.InputMethods.ShowFlags.Implicit));
    });
    }

    ReplyDelete