Za pomocą interfejsu WindowInsetsCompat
Twoja 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 cały ekran. Dzięki temu obsługuje systemowe wstawki w oknach, takie jak paski systemowe i klawiatura ekranowa.
Sprawdzanie widoczności klawiatury
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; });
Synchronizuj animację z klawiaturą programową
Gdy użytkownik kliknie pole tekstowe, klawiatura przesunie się z dołu ekranu, jak w tym przykładzie:
Na przykładzie oznaczonym jako „Niezsynchronizowany” na rysunku 2 widać domyślne zachowanie w Androidzie 10 (poziom interfejsu API 29), w którym pole tekstowe i treści aplikacji są umieszczane w miejscu zamiast synchronizowane 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 płynniej, jak widać na rysunku 2 w przykładzie oznaczonym etykietą „Synchronizacja”.
Skonfiguruj WindowInsetsAnimationCompat.Callback
tak, aby widok był synchronizowany 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 WindowInsetsAnimationCompat.Callback
, a mianowicie: onPrepare()
, onStart()
, onProgress()
i onEnd()
.
Zacznij od wywołania onPrepare()
przed zmianą 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.
Ten fragment kodu zawiera 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
skonfigurowane w przypadku któregoś z widoków, jest ono już wywołane. To dobry moment na zapisanie końcowego stanu właściwości widoku.
Ten fragment kodu zawiera 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 wstawki zmieniają się w ramach animacji, więc można ją zastąpić i otrzymywać powiadomienie przy każdej klatce animacji. Zaktualizuj właściwości widoku, aby animacja była synchronizowana z klawiaturą.
Na tym etapie wszystkie zmiany układu są zakończone. 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.
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ć onEnd
. Metoda ta jest wywoływana
po zakończeniu animacji. To dobry moment na usunięcie tymczasowych zmian.
Dodatkowe materiały
- WindowInsetsAnimation na GitHubie.