Animating your keyboard (part 1)
New WindowInsets APIs for checking the keyboard (IME) visibility and size
New in Android 11 is the ability for apps to create seamless transitions between the on screen keyboard being opened and closed, and it’s all powered by lots of improvements to the
WindowInsets APIs in Android 11.
So let’s take a look at how you can add this sort of experience to your apps. There are three steps:
- First, we need to go edge-to-edge.
- The second step is for apps to start reacting to inset animations.
- And the third step is by apps taking control of and driving inset animations, if it makes sense for your app.
Each of these steps follow on from each other, so we’ll cover each in separate blog posts. In this first post, we’ll cover going edge-to-edge, and the related API changes in Android 11.
Last year we introduced the concept of going edge to edge, as a way for apps to make the most of the new gestural navigation in Android 10:
As a quick re-cap, going edge to edge results in your app drawing behind the system bars, like you can see on the left.
To quote myself from last year:
By going edge-to-edge, apps will instead be laid out behind the system bars. This is to allow your app content to shine through to create a more immersive experience for your users.
So what has going edge to edge got to do with the keyboard?
Well going edge to edge is actually more than just drawing behind the status and navigation bars. It’s apps taking responsibility for handling those pieces of system UI which might overlap with the app.
The two obvious examples being the status bar and navigation bar, which we mentioned earlier. Then we have the on-screen-keyboard, or IME as it is sometimes referred to; it’s just another piece of system UI to be aware of.
How do apps go edge to edge?
If we flash back to our guidance from last year, going edge to edge is made up of 3 tasks:
- Change system bar colors
- Request to be laid out fullscreen
- Handle visual conflicts
We’re going to skip the first task, because nothing has changed there since last year. The guidance for steps 2 & 3 has been updated with some changes in Android 11. Let’s take a look.
#2: Request to be laid out fullscreen
For the second step, apps needed to use the
systemUiVisibility API with a bunch of flags, to request to be laid out fullscreen:
If you were using this API and have updated your compile SDK version to 30, you’ll have seen that all of these APIs are now deprecated.
They’ve been replaced with a single function on Window called
Instead of the many flags, you now pass in a boolean:
false if apps want to handle any system window fitting (and thus go fullscreen).
So that’s the 2nd step updated.
#3: Handling visual conflicts
Now let’s look at the third step: avoiding overlaps with the system UI, which can be summarised as using the window insets to know where to move content to, to avoid conflicts with the system UI. On Android, insets are represented by the
WindowInsets class, and
WindowInsetsCompat in AndroidX
If we take a look at
WindowInsets before the updates from API 30, the most common inset type to use is the system window insets. These cover the status and navigation bars, and also the keyboard when it is open.
Here we’re fetching the system window insets, and then updating the view’s padding to match, which is a very common use case.
There are a number of other inset types available, including the recently added gesture insets from Android 10:
getInsets(type: Int)which will return the visible insets for the given types.
getInsetsIgnoringVisibility(type: Int)which returns the insets, regardless of whether they’re visible or not.
isVisible(type: Int)which returns
trueif the given type is visible.
We just mentioned ‘types’ a lot there. These are defined in the
WindowInsets.Type class as functions, each returning an integer flag. You can combine multiple types, using a bitwise
OR to query for combined types, which we’ll see in a minute.
So if we go back to our example from before, to update it to the new APIs, they become:
The IME type ⌨️
Now the keen eyed 👀 among may have been looking at this list of types, and been looking at one type in particular: the IME type.
Well we can finally answer this StackOverflow question, from over 10 years ago (fashionably late), about how to check the visibility of the keyboard. 🎉
Similarly if we want to find out the height, we can do that too:
If we need to listen to changes to the keyboard, we can use the normal
OnApplyWindowInsetsListener, and use the same functions:
Hiding/showing the keyboard
Since we’re on a roll of answering StackOverflow questions, how about this one from 11 years ago, of how to close the keyboard.
Here we are going to introduce another new API in Android 11, called
But hiding and showing the keyboard isn’t all that the controller can do…
Earlier we said that some of the
View.SYSTEM_UI_* flags have been deprecated in Android 11, replaced with a new API. Well there were a number of other
View.SYSTEM_UI flags available, related to changing the system UI appearance or visibility, including:
Similar to the others, these have also been deprecated too in API 30, replaced with APIs in
Instead of going through the migration for all of these flags, we’ll cover a few common scenarios and see how to update them:
Here you can see a drawing app, which hides the System UI to maximise the space available for drawing:
The app also uses immersive mode, allowing the user to swipe the system bars back in once hidden. To implement this using
WindowInsetsController we change the hide and show behaviour to
Status bar content color
The next scenario is around the status bar content color. Here you see two apps:
On the left the app has a dark status bar background, with light content like the time and icons. But if we instead want a light status bar background with dark content, like the right, we can use
If you instead want to set a dark status bar, by passing in
0 instead to clear the value.
Note: you could implement this in your theme instead, by setting the
android:windowLightStatusBarattribute. This might be preferable if you know the value won’t change.
APPEARANCE_LIGHT_NAVIGATION_BARS flag is available which provides the same functionality for the navigation bars.
WindowInsetsController in AndroidX?
Unfortunately a Jetpack version of this API does not exist yet, but we are working on it. Stay tuned.
Going edge-to-edge: ✔️
So that’s the first step done. In the next blog post we’ll investigate the second step: apps reacting to inset animations.