How to show/hide the onscreen keyboard in Android
The modern approach
With the release of Android R, the team at Google have included a set of new WindowInsets APIs which expands the control that we, as developers, have over the window insets. These include the status bar, navigation bar and on-screen keyboard.
There is now a direct way to check, toggle and listen for changes the visibility of windows insets. We can even control and react to the animation when the visibility changes, but that won’t be covered in this article (but perhaps in a future article!).
We’ll be looking at the input method editor (IME) input type specifically, which includes the on-screen keyboard, but the same approach can be used for other types of window insets.
Getting started
At the time of writing, Android R is running on less than 1% of devices, but thanks to the magic of AndroidX these APIs are also usable on devices with earlier versions of Android. We will focus on using the AndroidX implementation but you can find the equivalent APIs to use in the documentation.
In your app module’s build.gradle
file, add the following line under dependencies
. Any version 1.5.0 or later will work, so be sure to check the latest version and update it accordingly.
implementation "androidx.core:core-ktx:1.5.0-beta01"
Showing and hiding keyboard
We’ll start by accessing the WindowInsetsController
. The view passed in doesn’t have to be an EditText
specifically, but can be any view in the same hierarchy as the focused EditText
.
val insetsController = ViewCompat.getWindowInsetsController(view)
To show the on-screen keyboard we can then call:
insetsController?.show(WindowInsetsCompat.Type.ime())
And to hide the keyboard:
insetsController?.hide(WindowInsetsCompat.Type.ime())
It’s as simple as that 👍
Check keyboard visibility
For anyone who has tried to check the keyboard visibility in the past, you probably would have messed around with global layout listeners while lamenting the fact that there wasn’t a better way.
Finally there is a proper way. We start by accessing the root window insets:
val insets = ViewCompat.getRootWindowInsets(view)
Then we can check the visibility of the IME window inset:
insets?.isVisible(WindowInsetsCompat.Type.ime())
Listening for keyboard changes
In order to listen for changes in the keyboard visibility, we need to let the system know that we want to the app itself to be responsible for fitting the content view to the insets. We can do this by calling:
WindowCompat.setDecorFitsSystemWindows(window, false)
This will mean that the view will be drawn behind the on-screen keyboard and other system UI, but now that we are able to listen for keyboard changes we can move any conflicting views according. For example, if we have a button constrained to the bottom of the screen, referenced by the variable bottomButton
, we can do the following:
Here we are using a bitwise OR operation to access the insets of both the system bars and IME combined. This is because the button will likely be inset by both the navigation bar and the on-screen keyboard.
If we wanted the insets of just the on-screen keyboard we can simply pass in WindowInsetsCompat.Type.ime()
instead.
Conclusion
Controlling the on-screen keyboard is a function that is common in Android development, so these APIs are long overdue. They also provide us a reliable way to control new types of window insets going forward and using AndroidX we can use these APIs in older Android versions as well.
Is it too good to be true? Well, yes maybe. It’s certainly better than what we had previously, but caution is advised. According to the documentation, on devices with SDK level 29 or below, these APIs will only return approximations based on the information available. Additionally, at the time of writing, they will not work at all on SDK level 22 or below when using the IME type — perhaps in the future this will change (or those versions will become obsolete that it won’t matter anyway).