Za pomocą WindowInsetsCompat
aplikacja może wysyłać zapytania i sterować klawiaturą ekranową (znaną też jako IME) podobnie jak obsługuje ona paski systemowe. Aplikacja może również używać WindowInsetsAnimationCompat
do tworzenia płynnych przejść, gdy klawiatura programowa jest otwarta lub zamknięta.
Wymagania wstępne
Zanim ustawisz elementy sterujące i animacje dla klawiatury oprogramowania, skonfiguruj aplikację tak, aby wyświetlała się od krawędzi do krawędzi. Dzięki temu obsługuje systemowe wstawki w oknach, takie jak paski systemowe i klawiatura ekranowa.
Sprawdź widoczność oprogramowania klawiatury
Użyj WindowInsets
, aby sprawdzić widoczność klawiatury oprogramowania.
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ć polecenia ViewCompat.setOnApplyWindowInsetsListener
, aby obserwować zmiany w widoczności klawiatury programowej.
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 klika pole do wprowadzania tekstu, klawiatura przesuwa się z dołu ekranu w to miejsce, jak widać w tym przykładzie:
Przykład z etykietą „Niezsynchronizowana” na ilustracji 2 pokazuje domyślne zachowanie w Androidzie 10 (poziom interfejsu API 29), w którym pole tekstowe i zawartość aplikacji wpadają na swoje miejsce, zamiast synchronizować się z animacją klawiatury, co może przykuwać wzrok.
W Androidzie 11 (poziom interfejsu API 30) i nowszych możesz używać
WindowInsetsAnimationCompat
do synchronizowania przejścia w aplikacji za pomocą klawiatury przesuwającej w górę lub w dół z dołu ekranu. Wygląda to płynniej, jak widać w przykładzie z etykietą „Zsynchronizowane” na rysunku 2.
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. });
Jest kilka metod zastępowania w WindowInsetsAnimationCompat.Callback
, m.in. onPrepare()
, onStart()
, onProgress()
i onEnd()
.
Zacznij od wywołania onPrepare()
przed zmianą układu.
Funkcja onPrepare
jest wywoływana, gdy rozpoczyna się animacja wcięcia i przed ponownym umieszczeniem widoków ze względu na animację. Możesz jej użyć do zapisania stanu początkowego,
który w tym przypadku jest dolną współrzędną widoku.
![Obraz przedstawiający dolną współrzędną stanu początkowego widoku głównego.](https://developer.android.com/static/images/guide/navigation/software-keyboard-3.png?authuser=5&hl=pl)
onPrepare()
do rejestrowania stanu początkowego.
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 wcięcia. Możesz go użyć, by we wszystkich właściwościach widoku
ustawić stan końcowy po zmianach układu. Jeśli masz wywołanie zwrotne OnApplyWindowInsetsListener
ustawione dla dowolnego z widoków, jest ono już wywoływane w tym punkcie. To dobry moment na zapisanie końcowego stanu właściwości widoku.
![Obraz przedstawiający dolną współrzędną stanu końcowego widoku](https://developer.android.com/static/images/guide/navigation/software-keyboard-4.png?authuser=5&hl=pl)
onStart()
do rejestrowania stanu końcowego.
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 animowany widok był zsynchronizowany z klawiaturą.
Na tym etapie wszystkie zmiany układu są zakończone. Jeśli np. używasz parametru View.translationY
do przesunięcia widoku, wartość stopniowo maleje przy każdym wywołaniu tej metody, aż w końcu osiągnie pozycję 0
w pozycji układu pierwotnego.
onProgress()
.
Ten fragment kodu zawiera 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; }
Możesz też zastąpić zasadę onEnd
. Metoda ta jest wywoływana
po zakończeniu animacji. To dobry moment na usunięcie wszelkich tymczasowych zmian.
Dodatkowe materiały
- WindowInsetsAnimation na GitHubie.