Za pomocą interfejsu WindowInsetsCompat
aplikacja może wysyłać zapytania do klawiatury ekranowej (zwanej też IME) i sterować nią w sposób podobny do interakcji z paskami systemowymi. Aplikacja może też używać WindowInsetsAnimationCompat
, aby zapewnić płynne przejścia podczas otwierania i zamykania klawiatury.
Wymagania wstępne
Zanim skonfigurujesz sterowanie i animację klawiatury programowej, skonfiguruj aplikację tak, aby wypełniała ekran. Dzięki temu może obsługiwać wstawki okna systemowego, takie jak paski systemowe i klawiatura ekranowa.
Sprawdzanie widoczności klawiatury programowej
Aby sprawdzić widoczność klawiatury ekranowej, użyj skrótu WindowInsets
.
Kotlin
val insets = ViewCompat.getRootWindowInsets(view) ?: return val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
Java
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view); boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
Możesz też użyć ViewCompat.setOnApplyWindowInsetsListener
, aby obserwować zmiany w widoczności klawiatury.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -> val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom insets }
Java
ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> { boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom; return insets; });
Synchronizacja animacji z klawiaturą ekranową
Gdy użytkownik kliknie pole tekstowe, klawiatura przesunie się z dołu ekranu, jak pokazano w tym przykładzie:
Przykład oznaczony etykietą „Niezsynchronizowany” na rysunku 2 przedstawia domyślne działanie w Androidzie 10 (poziom interfejsu API 29), w którym pole tekstowe i treści aplikacji są umieszczane w miejscu zamiast synchronizowania się z animowanymi efektami klawiatury. Takie zachowanie może być nieprzyjemne dla oka.
W Androidzie 11 (poziom interfejsu API 30) i nowszych możesz użyć
WindowInsetsAnimationCompat
, aby zsynchronizować przejście aplikacji z klawiaturą przesuwaną w górę i w dół od dołu ekranu. Wygląda to bardziej płynnie, jak widać na rysunku 2 w przykładzie oznaczonym etykietą „Synchronizacja”.
Skonfiguruj widokWindowInsetsAnimationCompat.Callback
, aby był zsynchronizowany z animacją klawiatury.
Kotlin
ViewCompat.setWindowInsetsAnimationCallback( view, object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) { // Override methods. } )
Java
ViewCompat.setWindowInsetsAnimationCallback( view, new WindowInsetsAnimationCompat.Callback( WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP ) { // Override methods. });
Istnieje kilka metod zastąpienia w obiekcie WindowInsetsAnimationCompat.Callback
, a mianowicie: onPrepare()
, onStart()
, onProgress()
i onEnd()
.
Zacznij od wywołania funkcji onPrepare()
, zanim wprowadzisz jakiekolwiek zmiany układu.
onPrepare
jest wywoływany, gdy rozpoczyna się animacja wstawionych elementów, i zanim widoki zostaną ponownie rozmieszczone z powodu animacji. Możesz użyć tego pola, aby zapisać stan początkowy, którym w tym przypadku jest dolna współrzędna widoku.

onPrepare()
do zarejestrowania stanu początkowego.
Ten fragment kodu pokazuje przykładowe wywołanie funkcji onPrepare
:
Kotlin
var startBottom = 0f override fun onPrepare( animation: WindowInsetsAnimationCompat ) { startBottom = view.bottom.toFloat() }
Java
float startBottom; @Override public void onPrepare( @NonNull WindowInsetsAnimationCompat animation ) { startBottom = view.getBottom(); }
Funkcja onStart
jest wywoływana, gdy rozpoczyna się animacja wstawionych elementów. Możesz użyć go do ustawienia wszystkich właściwości widoku na stan końcowy zmian układu. Jeśli masz wywołanie zwrotne OnApplyWindowInsetsListener
ustawione dla dowolnego z widoków, jest ono już wywołane. To dobry moment na zapisanie końcowego stanu właściwości widoku.

onStart()
, aby nagrać stan końcowy.
Ten fragment kodu pokazuje przykładowe wywołanie funkcji onStart
:
Kotlin
var endBottom = 0f override fun onStart( animation: WindowInsetsAnimationCompat, bounds: WindowInsetsAnimationCompat.BoundsCompat ): WindowInsetsAnimationCompat.BoundsCompat { // Record the position of the view after the IME transition. endBottom = view.bottom.toFloat() return bounds }
Java
float endBottom; @NonNull @Override public WindowInsetsAnimationCompat.BoundsCompat onStart( @NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds ) { endBottom = view.getBottom(); return bounds; }
Funkcja onProgress
jest wywoływana, gdy w ramach animacji zmieniają się wstawki. Możesz ją zastąpić i być powiadamiany o każdej klatce podczas animacji klawiatury. Zaktualizuj właściwości widoku, aby animacja była synchronizowana z klawiaturą.
W tej chwili wszystkie zmiany układu zostały wprowadzone. Jeśli np. użyjesz View.translationY
, aby zmienić widok, wartość będzie stopniowo maleć przy każdym wywołaniu tej metody i ostatecznie osiągnie 0
, czyli pierwotną pozycję układu.
onProgress()
do synchronizowania animacji.
Ten fragment kodu pokazuje przykładowe wywołanie funkcji onProgress
:
Kotlin
override fun onProgress( insets: WindowInsetsCompat, runningAnimations: MutableList<WindowInsetsAnimationCompat> ): WindowInsetsCompat { // Find an IME animation. val imeAnimation = runningAnimations.find { it.typeMask and WindowInsetsCompat.Type.ime() != 0 } ?: return insets // Offset the view based on the interpolated fraction of the IME animation. view.translationY = (startBottom - endBottom) * (1 - imeAnimation.interpolatedFraction) return insets }
Java
@NonNull @Override public WindowInsetsCompat onProgress( @NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations ) { // Find an IME animation. WindowInsetsAnimationCompat imeAnimation = null; for (WindowInsetsAnimationCompat animation : runningAnimations) { if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) { imeAnimation = animation; break; } } if (imeAnimation != null) { // Offset the view based on the interpolated fraction of the IME animation. view.setTranslationY((startBottom - endBottom) * (1 - imeAnimation.getInterpolatedFraction())); } return insets; }
Opcjonalnie możesz zastąpić wartość onEnd
. Ta metoda jest wywoływana po zakończeniu animacji. To dobry moment na usunięcie tymczasowych zmian.
Dodatkowe materiały
- WindowInsetsAnimation na GitHub.