Utilizzando WindowInsetsCompat,
la tua app può eseguire query e controllare la tastiera sullo schermo (chiamata anche
IME) in modo simile
al modo in cui interagisce con le barre di sistema. La tua app può anche utilizzare
WindowInsetsAnimationCompat
per creare transizioni fluide quando la tastiera su schermo viene aperta o chiusa.
Prerequisiti
Prima di configurare il controllo e l'animazione per la tastiera su schermo, configura l'app in modo che venga visualizzata edge-to-edge. In questo modo, può gestire gli inset delle finestre di sistema, come le barre di sistema e la tastiera sullo schermo.
Animazione predefinita degli inset sincronizzati
A partire dal livello API 37.2, le applicazioni che utilizzano le regolazioni predefinite del layout della finestra (come SOFT_INPUT_ADJUST_RESIZE o SOFT_INPUT_ADJUST_PAN quando mostrano l'IME utilizzando WindowInsetsController.show(WindowInsets.Type.ime())) senza implementare un WindowInsetsAnimationCompat.Callback personalizzato possono usufruire di animazioni frame per frame sincronizzate a livello di sistema. Se abilitata, questa funzionalità applica automaticamente gli inset a ogni frame e attiva un passaggio di layout, creando una transizione fluida mentre la tastiera si muove.
Per attivare questo comportamento, aggiungi il seguente tag <property> alla tua attività o applicazione nel file AndroidManifest.xml:
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
Esegui le animazioni degli inset al di fuori del thread principale
Per impostazione predefinita, la registrazione di un
WindowInsetsAnimationCompat.Callback impone che l'animazione degli inset della finestra venga eseguita sul thread principale dell'app. Questo è necessario se la tua app aggiorna e sincronizza continuamente il layout della visualizzazione durante l'animazione (ad esempio nel metodo onProgress). Tuttavia, se la tua app deve solo ascoltare le transizioni del ciclo di vita (ad esempio quando l'IME inizia o termina l'animazione) e non deve aggiornare le visualizzazioni frame per frame, l'esecuzione sul thread principale può causare overhead non necessario e stuttering dell'animazione, soprattutto se il thread principale è occupato.
A partire dal livello API 37.2, puoi utilizzare
ViewTreeObserver.WindowInsetsAnimationListener
per osservare queste transizioni. Poiché questo listener non riceve aggiornamenti sull'avanzamento frame per frame (nessun callback onProgress), il sistema può eseguire l'animazione degli inserti su un thread di animazione dedicato anziché sul thread principale, con una transizione più fluida.
Kotlin
val listener = object : ViewTreeObserver.WindowInsetsAnimationListener { override fun onPrepare(animation: WindowInsetsAnimation) { // Handle preparation before animation starts } override fun onEnd(animation: WindowInsetsAnimation) { // Clean up temporary changes } } // Add the listener view.viewTreeObserver.addWindowInsetsAnimationListener(listener) // Remove the listener when no longer needed view.viewTreeObserver.removeWindowInsetsAnimationListener(listener)
Java
ViewTreeObserver.WindowInsetsAnimationListener listener = new ViewTreeObserver.WindowInsetsAnimationListener() { @Override public void onPrepare(@NonNull WindowInsetsAnimation animation) { // Handle preparation before animation starts } @Override public void onEnd(@NonNull WindowInsetsAnimation animation) { // Clean up temporary changes } }; // Add the listener view.getViewTreeObserver().addWindowInsetsAnimationListener(listener); // Remove the listener when no longer needed view.getViewTreeObserver().removeWindowInsetsAnimationListener(listener);
Controlla la visibilità del software della tastiera
Utilizza WindowInsets per controllare la visibilità della tastiera su schermo.
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;
In alternativa, puoi utilizzare
ViewCompat.setOnApplyWindowInsetsListener
per osservare le modifiche alla visibilità della tastiera su schermo.
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; });
Sincronizza l'animazione con la tastiera su schermo
Quando un utente tocca un campo di immissione di testo, la tastiera scorre in posizione dalla parte inferiore dello schermo, come mostrato nell'esempio seguente:
L'esempio con l'etichetta "Non sincronizzato" nella figura 2 mostra il comportamento predefinito in Android 10 (livello API 29), in cui il campo di testo e i contenuti dell'app si posizionano anziché sincronizzarsi con l'animazione della tastiera, un comportamento che può essere visivamente fastidioso.
In Android 11 (livello API 30) e versioni successive, puoi utilizzare
WindowInsetsAnimationCompatper sincronizzare la transizione dell'app con lo scorrimento della tastiera verso l'alto e verso il basso dalla parte inferiore dello schermo. In questo modo, l'animazione appare più fluida, come mostrato nell'esempio "Sincronizzato" nella Figura 2.
Configura WindowInsetsAnimationCompat.Callback con la visualizzazione da sincronizzare con l'animazione della tastiera.
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. });
Esistono diversi metodi per eseguire l'override in WindowInsetsAnimationCompat.Callback, ovvero onPrepare(), onStart(), onProgress() e onEnd().
Inizia chiamando onPrepare() prima di qualsiasi modifica al layout.
onPrepare viene chiamato quando inizia un'animazione degli inset e prima che le visualizzazioni vengano riorganizzate a causa di un'animazione. Puoi utilizzarlo per salvare lo stato iniziale, che in questo caso è la coordinata inferiore della visualizzazione.
onPrepare() per registrare lo stato iniziale.
Il seguente snippet mostra una chiamata di esempio a 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(); }
onStart viene chiamato quando inizia un'animazione degli inset. Puoi utilizzarlo per impostare tutte le proprietà della visualizzazione sullo stato finale delle modifiche al layout. Se hai impostato un callback OnApplyWindowInsetsListener per una delle visualizzazioni, questo viene già chiamato a questo punto. Questo è un buon momento per salvare lo stato finale delle proprietà della visualizzazione.
onStart() per registrare lo stato finale.
Il seguente snippet mostra una chiamata di esempio a 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; }
onProgress viene chiamato quando gli inset cambiano durante l'esecuzione di un'animazione,
quindi puoi eseguirne l'override e ricevere una notifica per ogni frame durante l'animazione
della tastiera. Aggiorna le proprietà della visualizzazione in modo che l'animazione della visualizzazione sia sincronizzata con la tastiera.
A questo punto, tutte le modifiche al layout sono state completate. Ad esempio, se utilizzi View.translationY per spostare la visualizzazione, il valore diminuisce gradualmente per ogni chiamata di questo metodo e alla fine raggiunge 0 nella posizione di layout originale.
onProgress() per sincronizzare le animazioni.
Il seguente snippet mostra una chiamata di esempio a 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; }
Se vuoi, puoi eseguire l'override di onEnd. Questo metodo viene chiamato al termine dell'animazione. Questo è il momento giusto per eliminare eventuali modifiche temporanee.
Risorse aggiuntive
- WindowInsetsAnimation su GitHub.