The Android soft keyboard has a special talent for appearing exactly when you don't want it and hiding exactly when you do. You tap a button after filling in a form — keyboard stays up. You navigate to a search screen — keyboard doesn't appear. You open a dialog — keyboard blocks the submit button.
Sound familiar? Controlling the keyboard programmatically is one of those deceptively simple tasks that comes with a surprising number of edge cases. This guide covers everything — showing, hiding, detecting visibility, Fragment support, the modern API for Android 11+, and every windowSoftInputMode value worth knowing.
Quick Reference — Which API to Use?
| Android Version | Recommended API | Notes |
|---|---|---|
| Android 11+ (API 30+) | WindowInsetsController |
Modern, cleaner API |
| Android 10 and below | InputMethodManager |
Legacy but still widely used |
Hiding the Keyboard
In an Activity
import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
fun Activity.hideKeyboard() {
val view = currentFocus ?: View(this)
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
// Usage — call from anywhere in your Activity
hideKeyboard()
In a Fragment
fun Fragment.hideKeyboard() {
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE)
as InputMethodManager
val view = requireActivity().currentFocus ?: return
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
// Usage — call from anywhere in your Fragment
hideKeyboard()
From any View (utility function)
fun View.hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE)
as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0)
}
// Usage
binding.btnSubmit.setOnClickListener {
it.hideKeyboard()
// process form...
}
Showing the Keyboard
Show keyboard and focus an EditText
fun View.showKeyboard() {
requestFocus()
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE)
as InputMethodManager
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
}
// Usage — show keyboard when search screen opens
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.etSearch.postDelayed({
binding.etSearch.showKeyboard()
}, 200) // Small delay needed for fragment transition to complete
}
showSoftInput immediately in onViewCreated, the view may not be attached to the window yet and the keyboard won't appear. A small delay of 100–200ms ensures the view is ready.
Modern API — WindowInsetsController (Android 11+)
From Android 11 (API 30), Google introduced WindowInsetsController as the modern replacement for InputMethodManager. It's cleaner, more predictable, and part of the new insets API that handles everything from system bars to the keyboard:
import android.os.Build
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
// Use WindowInsetsControllerCompat for backward compatibility
fun Activity.hideKeyboardModern() {
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.hide(WindowInsetsCompat.Type.ime())
}
fun Activity.showKeyboardModern() {
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.show(WindowInsetsCompat.Type.ime())
}
// In Fragment
fun Fragment.hideKeyboardModern() {
val controller = WindowCompat.getInsetsController(
requireActivity().window,
requireView()
)
controller.hide(WindowInsetsCompat.Type.ime())
}
WindowInsetsControllerCompat from AndroidX Core — it handles backward compatibility automatically and works on older API levels too. Add the dependency if you don't have it: implementation 'androidx.core:core-ktx:1.13.0'
Detecting Keyboard Visibility Changes
Sometimes you need to know when the keyboard appears or disappears — to adjust layout, scroll a list to the focused field, or animate a button. Here's how to listen for keyboard visibility changes using the modern insets API:
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
// In Activity or Fragment
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
if (imeVisible) {
// Keyboard is showing — imeHeight is the keyboard height in pixels
binding.btnSubmit.translationY = -imeHeight.toFloat()
} else {
// Keyboard is hidden
binding.btnSubmit.translationY = 0f
}
insets
}
For this to work correctly, make sure your root view draws behind system bars:
// In Activity.onCreate() — before setContentView WindowCompat.setDecorFitsSystemWindows(window, false)
windowSoftInputMode — All Values Explained
The windowSoftInputMode attribute in AndroidManifest.xml controls how the keyboard behaves when an Activity starts. It's split into two categories — initial state and resize behaviour — and you can combine one from each:
State values (keyboard visibility on launch)
| Value | Behaviour | Use When |
|---|---|---|
stateUnspecified |
System decides (default) | Most cases |
stateUnchanged |
Keep keyboard state from previous screen | Multi-step forms |
stateHidden |
Hide keyboard when Activity starts | Screens with EditText not immediately needed |
stateAlwaysHidden |
Always hide keyboard on start, even if user navigated back | Home/landing screens |
stateVisible |
Show keyboard when Activity starts | Search screens, chat input |
stateAlwaysVisible |
Always show keyboard on start | Dedicated search/input screens |
Resize values (how layout adjusts when keyboard appears)
| Value | Behaviour | Use When |
|---|---|---|
adjustUnspecified |
System decides (default) | Most cases |
adjustResize |
Resizes the Activity window to make room for keyboard | Chat screens, comment boxes |
adjustPan |
Pans the window up to keep focused view visible | Fixed-height layouts, login screens |
adjustNothing |
Layout doesn't adjust at all | Full-screen apps managing insets manually |
Combining values in AndroidManifest.xml
<!-- Hide keyboard on start, resize layout when it appears -->
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateHidden|adjustResize"/>
<!-- Show keyboard immediately, pan layout up -->
<activity
android:name=".SearchActivity"
android:windowSoftInputMode="stateVisible|adjustPan"/>
Common Use Cases
Hide keyboard when tapping outside an EditText
// In your Activity — override dispatchTouchEvent
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val view = currentFocus
if (view is EditText) {
val rect = Rect()
view.getGlobalVisibleRect(rect)
if (!rect.contains(event.rawX.toInt(), event.rawY.toInt())) {
view.clearFocus()
hideKeyboard()
}
}
}
return super.dispatchTouchEvent(event)
}
Hide keyboard when pressing Done/Enter on EditText
binding.etSearch.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
actionId == EditorInfo.IME_ACTION_DONE) {
hideKeyboard()
performSearch(binding.etSearch.text.toString())
true
} else false
}
Show keyboard automatically on a search screen
class SearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Show keyboard as soon as search screen opens
binding.etSearch.post {
binding.etSearch.requestFocus()
binding.etSearch.showKeyboard()
}
}
override fun onDestroyView() {
super.onDestroyView()
// Always clean up — hide keyboard when leaving the screen
hideKeyboard()
}
}
Best Practices
- Always clean up in onDestroyView() — if you show the keyboard when a Fragment appears, hide it when it's destroyed. Leaving the keyboard open after navigation feels broken and jarring to users.
- Use post() or postDelayed() when showing keyboard — calling
showSoftInputbefore the view is attached to the window silently fails. Wrap it inview.post { ... }or add a short delay after fragment transitions. - Prefer WindowInsetsControllerCompat on new projects — it's the modern API, it's backward compatible via AndroidX, and it integrates with the edge-to-edge insets system that's now the default in Android 15+.
- Use adjustResize for chat-style screens — when a keyboard appears in a chat UI, you want the message list to shrink and the input field to stay visible.
adjustResizehandles this automatically. - Don't use adjustPan with edge-to-edge — if your app uses
WindowCompat.setDecorFitsSystemWindows(window, false)for edge-to-edge,adjustPanandadjustResizemay not work as expected. Handle insets manually usingViewCompat.setOnApplyWindowInsetsListenerinstead.
Frequently Asked Questions
Why is hideSoftInputFromWindow not working?
The most common reason is passing a null or incorrect view token. Make sure you're using activity.currentFocus?.windowToken. If no view has focus, the keyboard won't hide. Also ensure you're calling it on the main thread.
How do I show the keyboard automatically when a Fragment opens?
Call requestFocus() and showSoftInput() inside view.post { } or with a short postDelayed of 100–200ms in onViewCreated(). The delay ensures the view is fully attached to the window before the keyboard appears.
What is the difference between adjustResize and adjustPan?
adjustResize shrinks the Activity's visible area to make room for the keyboard — the layout reflows. adjustPan scrolls the layout upward to keep the focused view visible without resizing. Use adjustResize for chat or form screens, adjustPan for fixed layouts.
How do I hide the keyboard when the user taps outside an EditText?
Override dispatchTouchEvent in your Activity. On ACTION_DOWN, check if the touch coordinates fall outside the currently focused EditText's bounds using getGlobalVisibleRect(). If they do, call clearFocus() and hideKeyboard().
- Use
InputMethodManagerfor Android 10 and below - Use
WindowInsetsControllerCompatfor Android 11+ — cleaner and future-proof - Always wrap showSoftInput in
view.post { }to avoid silent failures - Always hide keyboard in onDestroyView() if you showed it in onViewCreated()
- Use
adjustResizefor chat/form screens,adjustPanfor fixed layouts - Use
ViewCompat.setOnApplyWindowInsetsListenerto detect keyboard height changes - Don't mix adjustPan/adjustResize with edge-to-edge — handle insets manually instead