How to detect if the android keyboard is open
With the release of the Android 11 preview 2 and here it looks like platform behaviour for listening to changes in the keyboard is finally getting added! Under the hood this looks to use WindowInsets, so it’s likely to be backported to earlier versions of Android. THANK YOU GOOGLE!
You want to know, at this moment, if the android keyboard is open. Easy right? Unfortunately not. The android keyboard is a great piece of design, that handles much of its visibility internally — depending on input from the user rather than the developer. Which is a great system when it works…until it doesn’t.

Are you open?
Android provides no direct way to determine if the keyboard is open, so we have to get a little creative.
The View
class has a handy method called getWindowVisibleDisplayFrame
from which we can retrieve a rectangle which contains the portion of the view visible to the user. This takes account of system decorations that can be placed on top of the view (such as the keyboard).
val visibleBounds = Rect()
myView.getWindowVisibleDisplayFrame(visibleBounds)
From the visible rectangle, we can get the visible height. By comparing the visible height to the views actual height we can determine if the keyboard has been drawn on top of the view. (psst, this can also be used to find out how tall the keyboard is).
val heightDiff = myView.height - visibleBounds.height()
val isOpen = heightDiff > 0
As this is a creative solution, there is a possibility of false positives by using a direct comparison. For that reason, it’s good to add a margin of error.
val marginOfError = Math.round(this.convertDpToPx(50F))
heightDiff > marginOfError
One last thing, it would be really great if this piece of code was reusable in any activity. How can we do that?
If you’ve ever inspected a layout, you would have noticed that towards the root of every activity, there is a ViewGroup
with an id of android.R.id.content
. By using this view, we can abstract our keyboard checking logic into an extension function — reusable by any activity.
// Add these extension functions to an empty kotlin filefun Activity.getRootView(): View {
return findViewById<View>(android.R.id.content)
}fun Context.convertDpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}fun Activity.isKeyboardOpen(): Boolean {
val visibleBounds = Rect()
this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
val heightDiff = getRootView().height - visibleBounds.height()
val marginOfError = Math.round(this.convertDpToPx(50F))
return heightDiff > marginOfError
}
fun Activity.isKeyboardClosed(): Boolean {
return !this.isKeyboardOpen()
}
Go ahead and try the above in any activity.
Unfortunately, if you use android:windowSoftInputMode=”adjustResize"
these methods will be no use for you. This method relies on the underlying view staying the same height when the keyboard is drawn — adjustResize
causes the underlying view to change size.
So now we (mostly) know how to check if the keyboard is open or closed at a specific point in time. But how about if we want to listen to the exact moment when the keyboard opens or closes? Again, we have to get creative.
Listening to the keyboard

Deep inside android there’s a class called ViewTreeObserver.OnGlobalLayoutListener
which has the rather cryptic java doc of:
Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.
All we care about is that this interface receives a callback at the moment the keyboard opens or closes (plus every time the global state changes, whatever that means). In this callback, we can use our previous extension functions to check if the keyboard is open or closed.
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
// Keep a reference to the last state of the keyboard
private var lastState: Boolean = activity.isKeyboardOpen() /**
* Something in the layout has changed
* so check if the keyboard is open or closed
* and if the keyboard state has changed
* save the new state and invoke the callback
*/
override fun onGlobalLayout() {
val isOpen = activity.isKeyboardOpen() if (isOpen == lastState) {
return
} else {
dispatchKeyboardEvent(isOpen)
lastState = isOpen
}
}
}
Of course, the global layout listener needs registered and onResume is as good a place as any.
override fun onResume() {
super.onResume()
val view = getRootView()
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
}
And we also need to unregister the listener.
override fun onPause() {
super.onPause()
val view = getRootView()
view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
This is a fair amount of boiler plate code, that doesn’t rely on any specific screen. It would be great if we could make it reusable right?
A component, that can be used by any activity in our app. Or even be transferred into a whole new project. While we’re at it, lets make the component lifecycle aware, so there’s no need to unregister our listener.
// Add android lifecycle libraries
"androidx.lifecycle:lifecycle-common:$vLifecycleVersion"
"androidx.lifecycle:lifecycle-extensions:$vLifecycleVersion"
kapt "androidx.lifecycle:lifecycle-compiler:$vLifecycleVersion"class KeyboardEventListener(
private val activity: AppCompatActivity,
private val callback: (isOpen: Boolean) -> Unit
) : LifecycleObserver {
private val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
private var lastState: Boolean = activity.isKeyboardOpen()
override fun onGlobalLayout() {
val isOpen = activity.isKeyboardOpen()
if (isOpen == lastState) {
return
} else {
dispatchKeyboardEvent(isOpen)
lastState = isOpen
}
}
}
init {
// Dispatch the current state of the keyboard
dispatchKeyboardEvent(activity.isKeyboardOpen()) // Make the component lifecycle aware
activity.lifecycle.addObserver(this) registerKeyboardListener()
}
private fun registerKeyboardListener() {
activity.getRootView().viewTreeObserver.addOnGlobalLayoutListener(listener)
}
private fun dispatchKeyboardEvent(isOpen: Boolean) {
when {
isOpen -> callback(true)
!isOpen -> callback(false)
}
}
@OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
@CallSuper
fun onLifecyclePause() {
unregisterKeyboardListener()
}
private fun unregisterKeyboardListener() {
activity.getRootView().viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
And to use just add the following to any activity.
override fun onResume() {
super.onResume()
KeyboardEventListener(this) { isOpen -> // handle event }
}
A word of warning
As this is a creative solution, there is a possibility that this solution could break in future versions of android. Even in current versions of android there will be plenty a project setups which render this solution useless. Hopefully, one day, google makes many a developer happy and exposes this behaviour natively!
How do you solve this problem?
Knowing when the keyboard is open or closed has been a problem that’s affected android developers since the beginning of the times. So there must be a wealth of workarounds — if there’s a method you like in particular, I’d love to see how it works, drop a comment below!