À l'aide de WindowInsetsCompat, votre application peut interroger et contrôler le clavier à l'écran (également appelé IME) de la même manière qu'elle interagit avec les barres système. Votre application peut également utiliser WindowInsetsAnimationCompat pour créer des transitions fluides lorsque le clavier virtuel est ouvert ou fermé.
Prérequis
Avant de configurer le contrôle et l'animation du clavier virtuel, configurez votre application pour qu'elle s'affiche de bord à bord. Cela lui permet de gérer les encarts de fenêtre système, tels que les barres système et le clavier à l'écran.
Animation d'encarts synchronisés par défaut
À partir du niveau d'API 37.2, les applications qui utilisent les ajustements de mise en page de la fenêtre par défaut (comme SOFT_INPUT_ADJUST_RESIZE ou SOFT_INPUT_ADJUST_PAN lors de l'affichage de l'IME à l'aide de WindowInsetsController.show(WindowInsets.Type.ime())) sans implémenter de WindowInsetsAnimationCompat.Callback personnalisé peuvent bénéficier d'animations synchronisées image par image au niveau du système. Lorsqu'elle est activée, cette fonctionnalité applique automatiquement les encarts sur chaque frame et déclenche une passe de mise en page, ce qui crée une transition fluide lorsque le clavier se déplace.
Pour activer ce comportement, ajoutez la balise <property> suivante à votre activité ou application dans le fichier AndroidManifest.xml :
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
Effectuer des animations d'encarts en dehors du thread principal
Par défaut, l'enregistrement d'un WindowInsetsAnimationCompat.Callback applique l'exécution de l'animation d'encart de fenêtre sur le thread principal de l'application. Cela est nécessaire si votre application met à jour et synchronise en continu la mise en page de sa vue pendant l'animation (par exemple, dans la méthode onProgress). Toutefois, si votre application n'a besoin que d'écouter les transitions de cycle de vie (par exemple, lorsque l'IME démarre ou arrête l'animation) et n'a pas besoin de mettre à jour les vues image par image, l'exécution sur le thread principal peut entraîner une surcharge inutile et des saccades d'animation, en particulier si le thread principal est occupé.
À partir du niveau d'API 37.2, vous pouvez utiliser ViewTreeObserver.WindowInsetsAnimationListener pour observer ces transitions. Étant donné que cet écouteur ne reçoit pas de mises à jour de progression image par image (pas de rappel onProgress), le système peut exécuter l'animation d'encart sur un thread d'animation dédié plutôt que sur le thread principal, ce qui permet une transition plus fluide.
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);
Vérifier la visibilité du clavier virtuel
Utilisez WindowInsets pour vérifier la visibilité du clavier virtuel.
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;
Vous pouvez également utiliser ViewCompat.setOnApplyWindowInsetsListener pour observer les modifications apportées à la visibilité du clavier virtuel.
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; });
Synchroniser l'animation avec le clavier virtuel
Lorsqu'un utilisateur appuie sur un champ de saisie de texte, le clavier s'affiche en bas de l'écran, comme illustré dans l'exemple suivant :
L'exemple intitulé "Non synchronisé" de la figure 2 montre le comportement par défaut dans Android 10 (niveau d'API 29), dans lequel le champ de texte et le contenu de l'application s'alignent au lieu de se synchroniser avec l'animation du clavier, un comportement qui peut être visuellement déroutant.
Sous Android 11 (niveau d'API 30) ou version ultérieure, vous pouvez utiliser
WindowInsetsAnimationCompatpour synchroniser la transition de l'application avec le clavier qui glisse vers le haut et vers le bas depuis le bas de l'écran. Le résultat est plus fluide, comme illustré dans l'exemple intitulé "Synchronized" (Synchronisé) de la figure 2.
Configurez WindowInsetsAnimationCompat.Callback avec la vue à synchroniser avec l'animation du clavier.
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. });
Il existe plusieurs méthodes à remplacer dans WindowInsetsAnimationCompat.Callback, à savoir onPrepare(), onStart(), onProgress() et onEnd().
Commencez par appeler onPrepare() avant d'apporter des modifications à la mise en page.
onPrepare est appelé lorsqu'une animation d'encarts démarre et avant que les vues ne soient réorganisées en raison d'une animation. Vous pouvez l'utiliser pour enregistrer l'état de départ, qui correspond ici à la coordonnée inférieure de la vue.
onPrepare() pour enregistrer l'état de départ.
L'extrait de code suivant montre un exemple d'appel à 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 est appelé lorsqu'une animation d'encarts démarre. Vous pouvez l'utiliser pour définir toutes les propriétés de la vue sur l'état final des modifications de mise en page. Si un rappel OnApplyWindowInsetsListener est défini pour l'une des vues, il est déjà appelé à ce stade. C'est le bon moment pour enregistrer l'état final des propriétés de la vue.
onStart() pour enregistrer l'état final.
L'extrait de code suivant montre un exemple d'appel à 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 est appelé lorsque les encarts changent dans le cadre de l'exécution d'une animation. Vous pouvez donc le remplacer et être averti à chaque frame pendant l'animation du clavier. Mettez à jour les propriétés de la vue pour qu'elle s'anime en synchronisation avec le clavier.
Toutes les modifications de mise en page sont maintenant terminées. Par exemple, si vous utilisez View.translationY pour décaler la vue, la valeur diminue progressivement à chaque appel de cette méthode et finit par atteindre 0 à la position de mise en page d'origine.
onProgress() pour synchroniser les animations.
L'extrait de code suivant montre un exemple d'appel à 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; }
Vous pouvez éventuellement remplacer onEnd. Cette méthode est appelée une fois l'animation terminée. C'est le moment idéal pour nettoyer les modifications temporaires.
Ressources supplémentaires
- WindowInsetsAnimation sur GitHub.