Présentation de l'animation des propriétés

Essayer Compose
Jetpack Compose est le kit d'outils d'interface utilisateur recommandé pour Android. Découvrez comment utiliser des animations dans Compose.

Le système d'animation des propriétés est un framework robuste qui vous permet d'animer presque tout. Vous pouvez définir une animation pour modifier n'importe quelle propriété d'objet au fil du temps, qu'elle dessine ou non à l'écran. Une animation de propriété modifie la valeur d'une propriété (un champ d'un objet) sur une durée spécifiée. Pour animer un élément, vous devez spécifier sa propriété, comme sa position à l'écran, sa durée d'animation et les valeurs comprises entre ces deux éléments.

Le système d'animation des propriétés vous permet de définir les caractéristiques suivantes d'une animation:

  • Durée: vous pouvez spécifier la durée d'une animation. La durée par défaut est de 300 ms.
  • Interpolation temporelle: vous pouvez spécifier le mode de calcul des valeurs de la propriété en fonction du temps écoulé actuel de l'animation.
  • Nombre de répétitions et comportement: vous pouvez spécifier si une animation doit se répéter ou non lorsqu'elle atteint la fin d'une durée, ainsi que le nombre de répétitions. Vous pouvez également spécifier si vous souhaitez que l'animation soit lue en sens inverse. Si vous définissez cette option sur "Inverser", l'animation avance, puis recule de manière répétée, jusqu'à ce que le nombre de répétitions soit atteint.
  • Ensembles d'animateurs: vous pouvez regrouper des animations dans des ensembles logiques qui sont lus ensemble, de manière séquentielle ou après des délais spécifiés.
  • Délai d'actualisation des images: vous pouvez spécifier la fréquence d'actualisation des images de votre animation. La valeur par défaut est définie sur une actualisation toutes les 10 ms, mais la vitesse à laquelle votre application peut actualiser les frames dépend de l'affluence globale du système et de la vitesse à laquelle il peut traiter le minuteur sous-jacent.

Pour voir un exemple complet d'animation de propriété, consultez la classe ChangeColor dans l'exemple CustomTransition sur GitHub.

Fonctionnement de l'animation des propriétés

Voyons d'abord comment fonctionne une animation avec un exemple simple. La figure 1 illustre un objet hypothétique animé avec sa propriété x, qui représente son emplacement horizontal sur un écran. La durée de l'animation est définie sur 40 ms et la distance à parcourir est de 40 pixels. Toutes les 10 ms, ce qui correspond à la fréquence d'actualisation des images par défaut, l'objet se déplace horizontalement de 10 pixels. Au bout de 40 ms, l'animation s'arrête et l'objet se termine à la position horizontale 40. Voici un exemple d'animation avec interpolation linéaire, ce qui signifie que l'objet se déplace à une vitesse constante.

Figure 1 : Exemple d'animation linéaire

Vous pouvez également spécifier une interpolation non linéaire pour les animations. La figure 2 illustre un objet hypothétique qui accélère au début de l'animation et ralentit à la fin. L'objet se déplace toujours de 40 pixels en 40 ms, mais de manière non linéaire. Au début, cette animation accélère jusqu'au point médian, puis ralentit de ce point jusqu'à la fin. Comme le montre la figure 2, la distance parcourue au début et à la fin de l'animation est inférieure à celle du milieu.

Figure 2. Exemple d'animation non linéaire

Examinons en détail comment les composants importants du système d'animation des propriétés calculeraient des animations telles que celles illustrées ci-dessus. La figure 3 montre comment les classes principales fonctionnent les unes avec les autres.

Figure 3. Calcul des animations

L'objet ValueAnimator suit la chronologie de votre animation, par exemple sa durée d'exécution et la valeur actuelle de la propriété qu'elle anime.

ValueAnimator encapsule un TimeInterpolator, qui définit l'interpolation de l'animation, et un TypeEvaluator, qui définit le calcul des valeurs pour la propriété animée. Par exemple, dans la figure 2, la valeur TimeInterpolator utilisée serait AccelerateDecelerateInterpolator et la valeur TypeEvaluator serait IntEvaluator.

Pour lancer une animation, créez un élément ValueAnimator et attribuez-lui les valeurs de début et de fin pour la propriété que vous souhaitez animer, ainsi que la durée de l'animation. Lorsque vous appelez start(), l'animation commence. Pendant toute l'animation, ValueAnimator calcule une fraction écoulée entre 0 et 1, en fonction de la durée de l'animation et du temps écoulé. La fraction écoulée représente le pourcentage de temps de fin de l'animation (0 signifiant 0 % et 1 signifiant 100 %). Par exemple, dans la figure 1, la fraction écoulée à t = 10 ms serait de 0,25, car la durée totale est t = 40 ms.

Lorsque ValueAnimator a terminé le calcul d'une fraction écoulée, il appelle la TimeInterpolator actuellement définie pour calculer une fraction interpolée. Une fraction interpolée mappe la fraction écoulée à une nouvelle fraction qui tient compte de l'interpolation temporelle définie. Par exemple, dans la figure 2, comme l'animation accélère lentement, la fraction interpolée (environ 0,15) est inférieure à la fraction écoulée (0,25) à t = 10 ms. Dans la figure 1, la fraction interpolée est toujours identique à la fraction écoulée.

Lorsque la fraction interpolée est calculée, ValueAnimator appelle la TypeEvaluator appropriée pour calculer la valeur de la propriété que vous animez, en fonction de la fraction interpolée, de la valeur de départ et de la valeur de fin de l'animation. Par exemple, dans la figure 2, la fraction interpolée était de 0,15 à t = 10 ms. La valeur de la propriété à ce moment-là serait donc de 0,15 × (40 - 0), soit 6.

Différences entre l'animation des propriétés et l'animation des vues

Le système d'animation des vues ne permet d'animer que des objets View. Par conséquent, si vous souhaitez animer des objets non-View, vous devez implémenter votre propre code pour le faire. Le système d'animation des vues est également limité dans le fait qu'il n'expose que quelques aspects d'un objet View à animer, comme la mise à l'échelle et la rotation d'une vue, mais pas la couleur d'arrière-plan, par exemple.

Un autre inconvénient du système d'animation des vues est qu'il ne modifiait que l'endroit où la vue était dessinée, et non la vue elle-même. Par exemple, si vous avez animé un bouton pour qu'il se déplace sur l'écran, il s'affiche correctement, mais l'emplacement réel où vous pouvez cliquer ne change pas. Vous devez donc implémenter votre propre logique pour gérer cela.

Avec le système d'animation des propriétés, ces contraintes sont complètement supprimées, et vous pouvez animer n'importe quelle propriété de n'importe quel objet (vues et non vues). L'objet lui-même est en fait modifié. Le système d'animation des propriétés est également plus robuste dans la façon dont il exécute l'animation. De manière générale, vous attribuez des animateurs aux propriétés que vous souhaitez animer, telles que la couleur, la position ou la taille, et vous pouvez définir des aspects de l'animation, tels que l'interpolation et la synchronisation de plusieurs animateurs.

Le système d'animation des vues, cependant, prend moins de temps à configurer et nécessite moins de code à écrire. Si l'animation des vues effectue tout ce que vous devez faire ou si votre code existant fonctionne déjà comme vous le souhaitez, il n'est pas nécessaire d'utiliser le système d'animation des propriétés. Il peut également être judicieux d'utiliser les deux systèmes d'animation pour des situations différentes si le cas d'utilisation se présente.

Présentation de l'API

Vous trouverez la plupart des API du système d'animation des propriétés dans android.animation. Étant donné que le système d'animation des vues définit déjà de nombreux interpolateurs dans android.view.animation, vous pouvez également les utiliser dans le système d'animation des propriétés. Les tableaux suivants décrivent les principaux composants du système d'animation des propriétés.

La classe Animator fournit la structure de base pour créer des animations. Normalement, vous n'utilisez pas cette classe directement, car elle ne fournit que des fonctionnalités minimales qui doivent être étendues pour prendre en charge les valeurs d'animation. Les sous-classes suivantes étendent Animator:

Tableau 1. Animateurs

Classe Description
ValueAnimator Moteur de synchronisation principal de l'animation des propriétés, qui calcule également les valeurs de la propriété à animer. Elle dispose de toutes les fonctionnalités de base qui calculent les valeurs d'animation et contient les détails temporels de chaque animation, des informations sur la répétition d'une animation, les écouteurs qui reçoivent des événements de mise à jour et la possibilité de définir des types personnalisés à évaluer. Les propriétés d'animation se font en deux parties: le calcul des valeurs animées et la définition de ces valeurs sur l'objet et la propriété animés. ValueAnimator n'effectue pas le deuxième élément. Vous devez donc écouter les mises à jour des valeurs calculées par ValueAnimator et modifier les objets que vous souhaitez animer avec votre propre logique. Pour en savoir plus, consultez la section Animer avec ValueAnimator.
ObjectAnimator Sous-classe de ValueAnimator qui vous permet de définir un objet cible et une propriété d'objet à animer. Cette classe met à jour la propriété en conséquence lorsqu'elle calcule une nouvelle valeur pour l'animation. Vous souhaitez utiliser ObjectAnimator la plupart du temps, car cela facilite considérablement le processus d'animation des valeurs sur les objets cibles. Cependant, vous pouvez parfois vouloir utiliser ValueAnimator directement, car ObjectAnimator comporte quelques restrictions supplémentaires, telles que la présence de méthodes d'accesseur spécifiques sur l'objet cible.
AnimatorSet Fournit un mécanisme permettant de regrouper des animations afin qu'elles s'exécutent les unes par rapport aux autres. Vous pouvez configurer les animations pour qu'elles soient lues ensemble, de manière séquentielle ou après un délai spécifié. Pour en savoir plus, consultez la section Choréographie de plusieurs animations avec des ensembles d'animations.

Les évaluateurs indiquent au système d'animation des propriétés comment calculer les valeurs d'une propriété donnée. Elles utilisent les données temporelles fournies par une classe Animator, ainsi que les valeurs de début et de fin de l'animation, pour calculer les valeurs animées de la propriété en fonction de ces données. Le système d'animation des propriétés fournit les évaluateurs suivants:

Tableau 2. Évaluateurs

Classe/Interface Description
IntEvaluator Évaluateur par défaut permettant de calculer les valeurs des propriétés int.
FloatEvaluator Évaluateur par défaut permettant de calculer les valeurs des propriétés float.
ArgbEvaluator Évaluateur par défaut permettant de calculer les valeurs des propriétés de couleur représentées sous forme de valeurs hexadécimales.
TypeEvaluator Une interface vous permettant de créer votre propre évaluateur Si vous animez une propriété d'objet qui n'est pas une int, une float ou une couleur, vous devez implémenter l'interface TypeEvaluator pour spécifier comment calculer les valeurs animées de la propriété d'objet. Vous pouvez également spécifier une TypeEvaluator personnalisée pour int, float et des valeurs de couleur si vous souhaitez traiter ces types différemment du comportement par défaut. Pour en savoir plus sur la rédaction d'un évaluateur personnalisé, consultez la section Utiliser un TypeEvaluator.

Un interpolateur temporel définit la manière dont les valeurs spécifiques d'une animation sont calculées en tant que fonction du temps. Par exemple, vous pouvez spécifier que les animations se déroulent de manière linéaire sur toute l'animation, ce qui signifie que l'animation se déplace uniformément sur toute la durée, ou vous pouvez spécifier des animations pour qu'elles utilisent le temps non linéaire, par exemple une accélération au début et une décélération à la fin. Le tableau 3 décrit les interpolateurs contenus dans android.view.animation. Si aucun des interpolateurs fournis ne répond à vos besoins, implémentez l'interface TimeInterpolator et créez les vôtres. Pour en savoir plus sur l'écriture d'un interpolateur personnalisé, consultez la section Utiliser des interpolateurs.

Tableau 3 : Interpolateurs

Classe/Interface Description
AccelerateDecelerateInterpolator Interpolateur dont la fréquence de changement commence et se termine lentement, mais accélère au milieu.
AccelerateInterpolator Interpolateur dont la fréquence de changement commence lentement, puis accélère.
AnticipateInterpolator Un interpolateur dont le changement commence en arrière, puis avance.
AnticipateOvershootInterpolator Un interpolateur dont le changement commence en arrière, avance et dépasse la valeur cible, puis revient enfin à la valeur finale.
BounceInterpolator Un interpolateur dont la modification rebondit à la fin.
CycleInterpolator Interpolateur dont l'animation se répète pendant un nombre spécifié de cycles.
DecelerateInterpolator Un interpolateur dont la fréquence de changement commence rapidement, puis ralentit.
LinearInterpolator Un interpolateur dont la fréquence de changement est constante.
OvershootInterpolator Un interpolateur dont la modification avance et dépasse la dernière valeur, puis revient en conséquence.
TimeInterpolator Une interface qui vous permet d'implémenter votre propre interpolateur.

Animer à l'aide de ValueAnimator

La classe ValueAnimator vous permet d'animer des valeurs d'un certain type pendant toute la durée d'une animation en spécifiant un ensemble de valeurs int, float ou de couleur. Vous obtenez un ValueAnimator en appelant l'une de ses méthodes de fabrique: ofInt(), ofFloat() ou ofObject(). Par exemple :

Kotlin

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

Dans ce code, ValueAnimator commence à calculer les valeurs de l'animation, comprises entre 0 et 100, pour une durée de 1 000 ms, lorsque la méthode start() s'exécute.

Vous pouvez également spécifier un type personnalisé à animer en procédant comme suit:

Kotlin

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

Dans ce code, ValueAnimator commence à calculer les valeurs de l'animation, entre startPropertyValue et endPropertyValue, à l'aide de la logique fournie par MyTypeEvaluator pendant une durée de 1 000 ms, lorsque la méthode start() s'exécute.

Vous pouvez utiliser les valeurs de l'animation en ajoutant un AnimatorUpdateListener à l'objet ValueAnimator, comme indiqué dans le code suivant:

Kotlin

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

Java

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

Dans la méthode onAnimationUpdate(), vous pouvez accéder à la valeur d'animation mise à jour et l'utiliser dans une propriété de l'une de vos vues. Pour en savoir plus sur les écouteurs, consultez la section Écouteurs d'animation.

Animer à l'aide d'ObjectAnimator

ObjectAnimator est une sous-classe de ValueAnimator (décrite dans la section précédente). Elle combine le moteur de temps et le calcul de la valeur de ValueAnimator avec la possibilité d'animer une propriété nommée d'un objet cible. Cela facilite considérablement l'animation des objets, car vous n'avez plus besoin d'implémenter ValueAnimator.AnimatorUpdateListener, car la propriété animée se met à jour automatiquement.

L'instanciation d'un ObjectAnimator est semblable à une ValueAnimator, mais vous devez également spécifier l'objet et le nom de sa propriété (sous forme de chaîne), ainsi que les valeurs d'animation suivantes:

Kotlin

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

Java

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

Pour que les propriétés de ObjectAnimator soient correctement mises à jour, procédez comme suit:

  • La propriété d'objet que vous animez doit avoir une fonction setter (en camel case) sous la forme set<PropertyName>(). Étant donné que ObjectAnimator met automatiquement à jour la propriété pendant l'animation, il doit pouvoir y accéder avec cette méthode setter. Par exemple, si le nom de la propriété est foo, vous devez disposer d'une méthode setFoo(). Si cette méthode setter n'existe pas, trois options s'offrent à vous :
    • Ajoutez la méthode setter à la classe si vous disposez des droits pour le faire.
    • Utilisez une classe wrapper que vous avez le droit de modifier et faites en sorte que ce wrapper reçoive la valeur avec une méthode setter valide et la transfère à l'objet d'origine.
    • Utilisez ValueAnimator à la place.
  • Si vous ne spécifiez qu'une seule valeur pour le paramètre values... dans l'une des méthodes de fabrique ObjectAnimator, elle est considérée comme la valeur de fin de l'animation. Par conséquent, la propriété d'objet que vous animez doit comporter une fonction getter utilisée pour obtenir la valeur de départ de l'animation. La fonction getter doit se présenter au format get<PropertyName>(). Par exemple, si le nom de la propriété est foo, vous devez disposer d'une méthode getFoo().
  • Les méthodes getter (si nécessaire) et setter de la propriété que vous animez doivent fonctionner sur le même type que les valeurs de début et de fin que vous spécifiez dans ObjectAnimator. Par exemple, vous devez avoir targetObject.setPropName(float) et targetObject.getPropName() si vous construisez la ObjectAnimator suivante :
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • En fonction de la propriété ou de l'objet que vous animez, vous devrez peut-être appeler la méthode invalidate() sur une vue pour forcer l'écran à se redessiner avec les valeurs animées mises à jour. Pour ce faire, utilisez le rappel onAnimationUpdate(). Par exemple, l'animation de la propriété color d'un objet drawable n'entraîne la mise à jour de l'écran que lorsque cet objet se redessine. Tous les setters de propriété sur View, tels que setAlpha() et setTranslationX(), invalident correctement la vue. Vous n'avez donc pas besoin de l'invalider lorsque vous appelez ces méthodes avec de nouvelles valeurs. Pour en savoir plus sur les écouteurs, consultez la section Écouteurs d'animation.

Chorégraphie de plusieurs animations avec AnimatorSet

Dans de nombreux cas, la lecture d'une animation est souhaitable en fonction du début ou de la fin d'une autre animation. Le système Android vous permet de regrouper des animations dans un AnimatorSet, afin de pouvoir spécifier si les animations doivent être lancées simultanément, de manière séquentielle ou après un délai spécifié. Vous pouvez également imbriquer des objets AnimatorSet les uns dans les autres.

L'extrait de code suivant lit les objets Animator suivants de la manière suivante:

  1. Lit bounceAnim.
  2. Lecture simultanée de squashAnim1, squashAnim2, stretchAnim1 et stretchAnim2.
  3. Lit bounceBackAnim.
  4. Lit fadeAnim.

Kotlin

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

Java

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

Écouteurs d'animation

Vous pouvez écouter des événements importants pendant la durée d'une animation à l'aide des écouteurs décrits ci-dessous.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate() : appelé sur chaque image de l'animation. Écoutez cet événement pour utiliser les valeurs calculées générées par ValueAnimator lors d'une animation. Pour utiliser la valeur, interrogez l'objet ValueAnimator transmis à l'événement pour obtenir la valeur animée actuelle avec la méthode getAnimatedValue(). L'implémentation de cet écouteur est requise si vous utilisez ValueAnimator.

      En fonction de la propriété ou de l'objet que vous animez, vous devrez peut-être appeler invalidate() sur une vue pour forcer cette zone de l'écran à se redessiner avec les nouvelles valeurs animées. Par exemple, l'animation de la propriété color d'un objet drawable n'entraîne des mises à jour de l'écran que lorsque cet objet se redessine. Tous les setters de propriété sur View, tels que setAlpha() et setTranslationX(), invalident correctement la vue. Vous n'avez donc pas besoin de l'invalider lorsque vous appelez ces méthodes avec de nouvelles valeurs.

Si vous ne souhaitez pas mettre en œuvre toutes les méthodes de l'interface Animator.AnimatorListener, vous pouvez étendre la classe AnimatorListenerAdapter au lieu d'implémenter l'interface Animator.AnimatorListener. La classe AnimatorListenerAdapter fournit des implémentations vides des méthodes que vous pouvez choisir de remplacer.

Par exemple, l'extrait de code suivant crée un AnimatorListenerAdapter uniquement pour le rappel onAnimationEnd():

Kotlin

ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            balls.remove((animation as ObjectAnimator).target)
        }
    })
}

Java

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

Animer les modifications de mise en page des objets ViewGroup

Le système d'animation des propriétés permet d'animer les modifications apportées aux objets ViewGroup et d'animer facilement les objets View eux-mêmes.

Vous pouvez animer les modifications de mise en page dans un ViewGroup avec la classe LayoutTransition. Les vues d'un ViewGroup peuvent passer par une animation d'apparition et de disparition lorsque vous les ajoutez à un ViewGroup ou que vous les supprimez d'un ViewGroup, ou lorsque vous appelez la méthode setVisibility() d'une vue avec VISIBLE, INVISIBLE ou GONE. Les vues restantes dans le ViewGroup peuvent également s'animer à leur nouvelle position lorsque vous ajoutez ou supprimez des vues. Vous pouvez définir les animations suivantes dans un objet LayoutTransition en appelant setAnimator() et en transmettant un objet Animator avec l'une des constantes LayoutTransition suivantes:

  • APPEARING : indicateur indiquant l'animation qui s'exécute sur les éléments qui apparaissent dans le conteneur.
  • CHANGE_APPEARING : indicateur indiquant l'animation qui s'exécute sur des éléments modifiés en raison de l'apparition d'un nouvel élément dans le conteneur.
  • DISAPPEARING : indicateur indiquant l'animation qui s'exécute sur les éléments qui disparaissent du conteneur.
  • CHANGE_DISAPPEARING : indicateur indiquant l'animation qui s'exécute sur des éléments qui changent en raison de la disparition d'un élément du conteneur.

Vous pouvez définir vos propres animations personnalisées pour ces quatre types d'événements afin de personnaliser l'apparence de vos transitions de mise en page ou simplement demander au système d'animation d'utiliser les animations par défaut.

Pour définir l'attribut android:animateLayoutchanges sur true pour le ViewGroup, procédez comme suit:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

Définir cet attribut sur "true" anime automatiquement les vues ajoutées ou supprimées dans le ViewGroup, ainsi que les vues restantes dans le ViewGroup.

Animer les changements d'état de la vue à l'aide de StateListAnimator

La classe StateListAnimator vous permet de définir des animateurs qui s'exécutent lorsque l'état d'une vue change. Cet objet se comporte comme un wrapper pour un objet Animator, qui appelle cette animation chaque fois que l'état de la vue spécifié (par exemple, "appui" ou "sélectionné") change.

Le StateListAnimator peut être défini dans une ressource XML avec un élément <selector> racine et des éléments <item> enfants, qui spécifient chacun un état d'affichage différent défini par la classe StateListAnimator. Chaque <item> contient la définition d'un ensemble d'animation de propriété.

Par exemple, le fichier suivant crée un animateur de liste d'états qui modifie les échelles x et y de la vue lorsque l'utilisateur appuie dessus:

res/xml/animate_scale.xml.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

Pour associer l'animateur de liste d'états à une vue, ajoutez l'attribut android:stateListAnimator comme suit:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

Les animations définies dans animate_scale.xml sont désormais utilisées lorsque l'état de ce bouton change.

Pour attribuer un animateur de liste d'états à une vue de votre code, utilisez la méthode AnimatorInflater.loadStateListAnimator(), puis attribuez l'animateur à votre vue avec la méthode View.setStateListAnimator().

Au lieu d'animer les propriétés de la vue, vous pouvez lire une animation drawable entre les changements d'état à l'aide de AnimatedStateListDrawable. Certains widgets système d'Android 5.0 utilisent ces animations par défaut. L'exemple suivant montre comment définir un AnimatedStateListDrawable en tant que ressource XML:

<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

Utiliser un TypeEvaluator

Si vous souhaitez animer un type inconnu du système Android, vous pouvez créer votre propre évaluateur en implémentant l'interface TypeEvaluator. Les types connus du système Android sont int, float ou une couleur, qui sont compatibles avec les évaluateurs de type IntEvaluator, FloatEvaluator et ArgbEvaluator.

Il n'y a qu'une seule méthode à implémenter dans l'interface TypeEvaluator : la méthode evaluate(). Cela permet à l'animateur que vous utilisez de renvoyer une valeur appropriée pour votre propriété animée au point actuel de l'animation. La classe FloatEvaluator montre comment procéder:

Kotlin

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Java

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

Remarque:Lorsque ValueAnimator (ou ObjectAnimator) s'exécute, il calcule une fraction actuelle de l'animation (une valeur comprise entre 0 et 1), puis une version interpolée de celle-ci en fonction de l'interpolateur que vous utilisez. La fraction interpolée est ce que votre TypeEvaluator reçoit via le paramètre fraction. Vous n'avez donc pas besoin de prendre en compte l'interpolateur lors du calcul des valeurs animées.

Utiliser des interpolateurs

Un interpolateur définit la manière dont les valeurs spécifiques d'une animation sont calculées en fonction du temps. Par exemple, vous pouvez spécifier que les animations se déroulent de manière linéaire sur toute l'animation, ce qui signifie que l'animation se déplace uniformément sur toute la durée, ou spécifier des animations pour qu'elles utilisent le temps non linéaire, par exemple en utilisant une accélération ou une décélération au début ou à la fin de l'animation.

Les interpolateurs du système d'animation reçoivent de la part des animateurs une fraction qui représente le temps écoulé de l'animation. Les interpolateurs modifient cette fraction pour qu'elle coïncide avec le type d'animation qu'elle vise à fournir. Le système Android fournit un ensemble d'interpolateurs courants dans android.view.animation package. Si aucune de ces options ne répond à vos besoins, vous pouvez implémenter l'interface TimeInterpolator et créer la vôtre.

Par exemple, la façon dont l'interpolateur par défaut AccelerateDecelerateInterpolator et le LinearInterpolator calculent les fractions interpolées sont comparées ci-dessous. LinearInterpolator n'a aucun effet sur la fraction écoulée. AccelerateDecelerateInterpolator accélère dans l'animation et en ralentit en dehors. Les méthodes suivantes définissent la logique de ces interpolateurs:

AccelerateDecelerateInterpolator

Kotlin

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

Java

@Override
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

Kotlin

override fun getInterpolation(input: Float): Float = input

Java

@Override
public float getInterpolation(float input) {
    return input;
}

Le tableau suivant représente les valeurs approximatives calculées par ces interpolateurs pour une animation de 1 000 ms:

ms écoulées Fraction écoulée/Fraction interpolée (linéaire) Fraction interpolée (accélération/décélération)
0 0 0
200 0,2 0,1
400 0,4 0,345
600 0,6 0,8
800 0,8 0,9
1 000 1 1

Comme le montre le tableau, LinearInterpolator modifie les valeurs à la même vitesse, 0,2 tous les 200 ms. AccelerateDecelerateInterpolator modifie les valeurs plus rapidement que LinearInterpolator entre 200 ms et 600 ms, et plus lentement entre 600 ms et 1 000 ms.

Spécifier des images clés

Un objet Keyframe consiste en une paire heure/valeur qui vous permet de définir un état spécifique à un moment précis d'une animation. Chaque image clé peut également avoir son propre interpolateur pour contrôler le comportement de l'animation dans l'intervalle entre l'heure de l'image clé précédente et celle de cette image clé.

Pour instancier un objet Keyframe, vous devez utiliser l'une des méthodes de fabrique, ofInt(), ofFloat() ou ofObject(), afin d'obtenir le type de Keyframe approprié. Appelez ensuite la méthode de fabrique ofKeyframe() pour obtenir un objet PropertyValuesHolder. Une fois l'objet en votre possession, vous pouvez obtenir un animateur en transmettant l'objet PropertyValuesHolder et l'objet à animer. L'extrait de code suivant montre comment procéder:

Kotlin

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Java

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

Animer des vues

Le système d'animation des propriétés permet une animation simplifiée des objets View et offre quelques avantages par rapport au système d'animation des vues. Le système d'animation des vues a transformé les objets View en modifiant leur manière de dessiner. Cette opération était effectuée dans le conteneur de chaque vue, car la vue elle-même n'avait aucune propriété à manipuler. Cela a entraîné l'animation de la vue, mais n'a entraîné aucune modification de l'objet View lui-même. Cela a entraîné un comportement tel qu'un objet existant toujours à son emplacement d'origine, même s'il était dessiné à un autre endroit à l'écran. Dans Android 3.0, de nouvelles propriétés et les méthodes getter et setter correspondantes ont été ajoutées pour éliminer cet inconvénient.

Le système d'animation des propriétés peut animer des vues à l'écran en modifiant les propriétés réelles des objets View. En outre, les vues appellent automatiquement la méthode invalidate() pour actualiser l'écran chaque fois que ses propriétés sont modifiées. Les nouvelles propriétés de la classe View qui facilitent les animations de propriétés sont les suivantes:

  • translationX et translationY: ces propriétés contrôlent l'emplacement de la vue en tant que delta par rapport à ses coordonnées gauche et supérieure, qui sont définies par son conteneur de mise en page.
  • rotation, rotationX et rotationY: ces propriétés contrôlent la rotation en 2D (propriété rotation) et en 3D autour du point pivot.
  • scaleX et scaleY: ces propriétés contrôlent la mise à l'échelle 2D d'une vue autour de son point pivot.
  • pivotX et pivotY: ces propriétés contrôlent l'emplacement du point pivot, autour duquel se produisent les transformations de rotation et de mise à l'échelle. Par défaut, le point pivot est au centre de l'objet.
  • x et y: ces propriétés utilitaires simples décrivent l'emplacement final de la vue dans son conteneur, sous la forme d'une somme des valeurs gauche et supérieure, et des valeurs translationX et translationY.
  • alpha: représente la transparence alpha sur la vue. Cette valeur est de 1 (opaque) par défaut, la valeur 0 représentant une transparence totale (non visible).

Pour animer une propriété d'un objet View, telle que sa couleur ou sa valeur de rotation, il vous suffit de créer un animateur de propriété et de spécifier la propriété View que vous souhaitez animer. Par exemple :

Kotlin

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Java

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

Pour en savoir plus sur la création d'animateurs, consultez les sections consacrées aux animations avec ValueAnimator et ObjectAnimator.

Animer à l'aide de ViewPropertyAnimator

ViewPropertyAnimator permet d'animer facilement plusieurs propriétés d'une View en parallèle, à l'aide d'un seul objet Animator sous-jacent. Il se comporte comme un ObjectAnimator, car il modifie les valeurs réelles des propriétés de la vue, mais il est plus efficace lors de l'animation simultanée de plusieurs propriétés. De plus, le code permettant d'utiliser ViewPropertyAnimator est beaucoup plus concis et plus facile à lire. Les extraits de code suivants montrent les différences dans l'utilisation de plusieurs objets ObjectAnimator, d'un seul ObjectAnimator et de ViewPropertyAnimator lors de l'animation simultanée des propriétés x et y d'une vue.

Plusieurs objets ObjectAnimator

Kotlin

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

Java

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

One ObjectAnimator

Kotlin

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

Java

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();

ViewPropertyAnimator

Kotlin

myView.animate().x(50f).y(100f)

Java

myView.animate().x(50f).y(100f);

Pour en savoir plus sur ViewPropertyAnimator, consultez l'article de blog des développeurs Android correspondant.

Déclarer des animations au format XML

Le système d'animation des propriétés vous permet de déclarer des animations de propriétés avec du code XML au lieu de le faire par programmation. Définir vos animations en XML vous permet de les réutiliser facilement dans plusieurs activités et de modifier plus facilement la séquence d'animation.

Pour distinguer les fichiers d'animation qui utilisent les nouvelles API d'animation de propriétés de ceux qui utilisent l'ancien framework view animation, à partir d'Android 3.1, vous devez enregistrer les fichiers XML des animations de propriétés dans le répertoire res/animator/.

Les classes d'animation de propriétés suivantes sont compatibles avec la déclaration XML avec les balises XML suivantes:

Pour trouver les attributs que vous pouvez utiliser dans votre déclaration XML, consultez la section Ressources d'animation. L'exemple suivant lit les deux ensembles d'animations d'objets de manière séquentielle, le premier ensemble imbriqué lisant deux animations d'objets:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

Pour exécuter cette animation, vous devez gonfler les ressources XML dans le code en un objet AnimatorSet, puis définir les objets cibles de toutes les animations avant de démarrer l'ensemble d'animation. Pour simplifier les choses, l'appel de setTarget() spécifie un objet cible unique pour tous les enfants de AnimatorSet. Pour ce faire, entrez le code suivant :

Kotlin

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

Java

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

Vous pouvez également déclarer un ValueAnimator au format XML, comme illustré dans l'exemple suivant:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

Pour utiliser la ValueAnimator précédente dans votre code, vous devez gonfler l'objet, ajouter une AnimatorUpdateListener, obtenir la valeur d'animation mise à jour et l'utiliser dans une propriété de l'une de vos vues, comme indiqué dans le code suivant:

Kotlin

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

Java

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

Pour en savoir plus sur la syntaxe XML permettant de définir des animations de propriétés, consultez la section Ressources d'animation .

Effets potentiels sur les performances de l'interface utilisateur

Les animateurs qui mettent à jour l'interface utilisateur entraînent un travail de rendu supplémentaire pour chaque image dans laquelle l'animation s'exécute. Par conséquent, l'utilisation d'animations gourmandes en ressources peut avoir un impact négatif sur les performances de votre application.

Les tâches requises pour animer votre interface utilisateur sont ajoutées à l'étape d'animation du pipeline de rendu. Pour savoir si vos animations ont un impact sur les performances de votre application, activez le rendu GPU du profil et surveillez l'étape d'animation. Pour en savoir plus, consultez la section Tutoriel du rendu GPU du profil.