Le système d'animation de propriétés est un framework robuste qui vous permet d'animer presque n'importe quoi. Vous pouvez définir une animation pour modifier n'importe quelle propriété d'objet au fil du temps, qu'elle s'affiche ou non à l'écran. Une animation de propriété modifie la valeur d'une propriété (un champ dans un objet) sur une durée spécifiée. Pour animer un élément, vous devez spécifier la propriété de l'objet que vous souhaitez animer (par exemple, sa position à l'écran), la durée de l'animation et les valeurs entre lesquelles vous souhaitez animer l'élément.
Le système d'animation de 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 la façon dont les valeurs de la propriété sont calculées en fonction du temps écoulé actuel de l'animation.
- Nombre et comportement des répétitions : vous pouvez spécifier si une animation doit se répéter lorsqu'elle atteint la fin d'une durée et combien de fois elle doit se répéter. Vous pouvez également spécifier si vous souhaitez que l'animation soit lue en sens inverse. Si vous le définissez sur "reverse", l'animation est lue d'avant en arrière de manière répétée jusqu'à ce que le nombre de répétitions soit atteint.
- Ensembles d'animation : 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 frames : vous pouvez spécifier la fréquence d'actualisation des frames de votre animation. Par défaut, l'actualisation est définie sur 10 ms, mais la vitesse à laquelle votre application peut actualiser les frames dépend en fin de compte de la charge globale du système et de la rapidité avec laquelle le système peut traiter le minuteur sous-jacent.
Pour obtenir un exemple complet d'animation de propriété, consultez la classe ChangeColor dans l'exemple CustomTransition sur GitHub.
Fonctionnement de l'animation de propriété
Commençons par voir comment fonctionne une animation avec un exemple simple. La figure 1 représente 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 (fréquence d'actualisation 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 retrouve à 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 que les animations doivent avoir une interpolation non linéaire. La figure 2 illustre un objet hypothétique qui accélère au début de l'animation et décélère à 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'à la moitié, puis décélère de la moitié jusqu'à la fin de l'animation. Comme le montre la figure 2, la distance parcourue au début et à la fin de l'animation est inférieure à celle parcourue au milieu.
Figure 2. Exemple d'animation non linéaire
Examinons en détail comment les composants importants du système d'animation de propriétés calculeraient les animations comme celles illustrées ci-dessus. La figure 3 montre comment les classes principales fonctionnent ensemble.
Figure 3. Calcul des animations
L'objet ValueAnimator suit le timing de votre animation, par exemple la durée d'exécution de l'animation et la valeur actuelle de la propriété qu'elle anime.
ValueAnimator encapsule un TimeInterpolator, qui définit l'interpolation d'animation, et un TypeEvaluator, qui définit comment calculer les valeurs de la propriété animée. Par exemple, dans la figure 2, le TimeInterpolator utilisé serait AccelerateDecelerateInterpolator et le TypeEvaluator serait IntEvaluator.
Pour démarrer une animation, créez un ValueAnimator et attribuez-lui les valeurs de début et de fin de la propriété que vous souhaitez animer, ainsi que la durée de l'animation. Lorsque vous appelez start(), l'animation commence. Pendant toute la durée de 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 écoulé pour l'animation (0 % pour 0 et 100 % pour 1). Par exemple, dans la figure 1, la fraction écoulée à t = 10 ms serait de 0,25, car la durée totale est de t = 40 ms.
Lorsque ValueAnimator a fini de calculer une fraction écoulée, il appelle le TimeInterpolator actuellement défini 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 égale à la fraction écoulée.
Lorsque la fraction interpolée est calculée, ValueAnimator appelle le TypeEvaluator approprié pour calculer la valeur de la propriété que vous animez, en fonction de la fraction interpolée, de la valeur de début et de la valeur de fin de l'animation. Par exemple, sur 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 de propriétés et l'animation de vues
Le système d'animation de vues ne permet d'animer que les objets View. Par conséquent, si vous souhaitez animer des objets non View, vous devez implémenter votre propre code. Le système d'animation de vues est également limité, car il n'expose que quelques aspects d'un objet View à animer, tels que la mise à l'échelle et la rotation d'une vue, mais pas la couleur d'arrière-plan, par exemple.View
Un autre inconvénient du système d'animation de vue est qu'il ne modifie que l'emplacement où la vue est 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, le bouton s'affiche correctement, mais l'emplacement réel où vous pouvez cliquer dessus ne change pas. Vous devez donc implémenter votre propre logique pour gérer cela.
Avec le système d'animation de propriété, ces contraintes sont complètement supprimées. Vous pouvez animer n'importe quelle propriété de n'importe quel objet (vues et non-vues), et l'objet lui-même est réellement modifié. Le système d'animation des propriétés est également plus robuste dans la façon dont il effectue 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. Vous pouvez également définir des aspects de l'animation, tels que l'interpolation et la synchronisation de plusieurs animateurs.
Toutefois, le système d'animation de vues est plus rapide à configurer et nécessite moins de code. Si l'animation de vue accomplit 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 de propriété. Il peut également être judicieux d'utiliser les deux systèmes d'animation pour différentes situations si le cas d'utilisation se présente.
Présentation de l'API
La plupart des API du système d'animation des propriétés se trouvent dans android.animation. Étant donné que le système d'animation de vue définit déjà de nombreux interpolateurs dans android.view.animation, vous pouvez également utiliser ces interpolateurs dans le système d'animation de propriété. Les tableaux suivants décrivent les principaux composants du système d'animation de propriétés.
La classe Animator fournit la structure de base pour créer des animations. En règle générale, vous n'utilisez pas cette classe directement, car elle ne fournit qu'une fonctionnalité minimale qui doit être étendue pour prendre entièrement en charge l'animation des valeurs. Les sous-classes suivantes étendent Animator :
Tableau 1. Animateurs
| Classe | Description |
|---|---|
ValueAnimator |
Moteur de minutage principal pour l'animation de propriété, qui calcule également les valeurs de la propriété à animer. Il possède toutes les fonctionnalités de base qui calculent les valeurs d'animation et contient les détails de synchronisation de chaque animation, des informations sur la répétition d'une animation, des écouteurs qui reçoivent des événements de mise à jour et la possibilité de définir des types personnalisés à évaluer. L'animation des propriétés se compose de 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 la deuxième partie. 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 grandement l'animation des valeurs sur les objets cibles. Toutefois, vous pouvez parfois utiliser ValueAnimator directement, car ObjectAnimator présente quelques restrictions supplémentaires, par exemple en exigeant que des méthodes d'accesseur spécifiques soient présentes sur l'objet cible. |
AnimatorSet |
Fournit un mécanisme permettant de regrouper les animations afin qu'elles s'exécutent les unes par rapport aux autres. Vous pouvez définir des 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égraphier plusieurs animations avec des ensembles d'Animator. |
Les évaluateurs indiquent au système d'animation de propriété comment calculer les valeurs d'une propriété donnée. Ils prennent les données de timing fournies par une classe Animator, la valeur de début et de fin de l'animation, et calculent les valeurs animées de la propriété en fonction de ces données. Le système d'animation de propriété fournit les évaluateurs suivants :
Tableau 2. Évaluateurs
| Classe/Interface | Description |
|---|---|
IntEvaluator |
Évaluateur par défaut pour calculer les valeurs des propriétés int. |
FloatEvaluator |
Évaluateur par défaut pour calculer les valeurs des propriétés float. |
ArgbEvaluator |
Évaluateur par défaut pour calculer les valeurs des propriétés de couleur représentées sous forme de valeurs hexadécimales. |
TypeEvaluator |
Interface 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é de l'objet. Vous pouvez également spécifier un TypeEvaluator personnalisé pour les valeurs int, float et de couleur, si vous souhaitez traiter ces types différemment du comportement par défaut.
Pour en savoir plus sur la façon d'écrire un évaluateur personnalisé, consultez la section Utiliser un TypeEvaluator. |
Un interpolateur temporel définit la façon 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 produisent de manière linéaire sur l'ensemble de l'animation, ce qui signifie que l'animation se déplace de manière uniforme tout le temps. Vous pouvez également spécifier que les animations utilisent un temps non linéaire, par exemple en accélérant au début et en décélérant à la fin de l'animation. 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 le vôtre. Pour savoir comment écrire un interpolateur personnalisé, consultez 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 le taux de variation commence lentement, puis s'accélère. |
AnticipateInterpolator |
Interpolateur dont le changement commence en arrière, puis avance. |
AnticipateOvershootInterpolator |
Un interpolateur dont le changement commence à l'envers, avance et dépasse la valeur cible, puis revient à la valeur finale. |
BounceInterpolator |
Un interpolateur dont le changement rebondit à la fin. |
CycleInterpolator |
Interpolateur dont l'animation se répète pendant un nombre défini de cycles. |
DecelerateInterpolator |
Interpolateur dont la fréquence de changement commence par être rapide, puis ralentit. |
LinearInterpolator |
Interpolateur dont la fréquence de changement est constante. |
OvershootInterpolator |
Un interpolateur dont le changement avance et dépasse la dernière valeur, puis revient en arrière. |
TimeInterpolator |
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 la durée d'une animation en spécifiant un ensemble de valeurs int, float ou de couleur à animer. Vous obtenez un ValueAnimator en appelant l'une de ses méthodes de fabrique : ofInt(), ofFloat() ou ofObject(). 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, 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 en utilisant 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 avec ObjectAnimator
ObjectAnimator est une sous-classe de ValueAnimator (abordée dans la section précédente). Elle combine le moteur de timing et le calcul de la valeur de ValueAnimator avec la possibilité d'animer une propriété nommée d'un objet cible. Cela facilite grandement l'animation de n'importe quel objet, car vous n'avez plus besoin d'implémenter ValueAnimator.AnimatorUpdateListener, car la propriété animée est mise à jour automatiquement.
L'instanciation d'un ObjectAnimator est semblable à celle d'un ValueAnimator, mais vous spécifiez également l'objet et le nom de la propriété de cet objet (sous forme de chaîne) ainsi que les valeurs entre lesquelles animer :
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 mise à jour ObjectAnimator soient correctes, vous devez procéder comme suit :
- La propriété d'objet que vous animez doit avoir une fonction setter (en camel case) au format
set<PropertyName>(). Étant donné queObjectAnimatormet automatiquement à jour la propriété pendant l'animation, il doit pouvoir accéder à la propriété avec cette méthode setter. Par exemple, si le nom de la propriété estfoo, vous devez disposer d'une méthodesetFoo(). Si cette méthode setter n'existe pas, vous avez trois options :- Ajoutez la méthode setter à la classe si vous en avez les droits.
- Utilisez une classe wrapper que vous êtes autorisé à modifier et faites en sorte que ce wrapper reçoive la valeur avec une méthode setter valide et la transmette à 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 fabriqueObjectAnimator, 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 disposer d'une fonction getter utilisée pour obtenir la valeur de départ de l'animation. La fonction getter doit être au formatget<PropertyName>(). Par exemple, si le nom de la propriété estfoo, vous devez disposer d'une méthodegetFoo(). - 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 pour
ObjectAnimator. Par exemple, vous devez disposer detargetObject.setPropName(float)ettargetObject.getPropName()si vous construisez leObjectAnimatorsuivant :ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- Selon la propriété ou 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 rappelonAnimationUpdate(). Par exemple, l'animation de la propriété de couleur 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 quesetAlpha()etsetTranslationX(), invalident correctement la vue. Vous n'avez donc pas besoin d'invalider la vue lorsque vous appelez ces méthodes avec de nouvelles valeurs. Pour en savoir plus sur les écouteurs, consultez la section Écouteurs d'animation.
Chorégraphier plusieurs animations à l'aide d'un AnimatorSet
Dans de nombreux cas, vous souhaitez lire une animation qui dépend du début ou de la fin d'une autre animation. Le système Android vous permet de regrouper des animations dans un AnimatorSet. Vous pouvez ainsi spécifier si les animations doivent démarrer simultanément, séquentiellement 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 :
- Joue
bounceAnim. - Lit
squashAnim1,squashAnim2,stretchAnim1etstretchAnim2en même temps. - Joue
bounceBackAnim. - Joue
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 les événements importants pendant la durée d'une animation avec les écouteurs décrits ci-dessous.
Animator.AnimatorListeneronAnimationStart()- Called when the animation starts.onAnimationEnd(): Appelé à la fin de l'animation.onAnimationRepeat(): appelé lorsque l'animation se répète.onAnimationCancel(): appelé lorsque l'animation est annulée. Une animation annulée appelle égalementonAnimationEnd(), quelle que soit la manière dont elle s'est terminée.
ValueAnimator.AnimatorUpdateListener-
onAnimationUpdate(): appelé sur chaque frame de l'animation. Écoutez cet événement pour utiliser les valeurs calculées générées parValueAnimatorlors d'une animation. Pour utiliser la valeur, interrogez l'objetValueAnimatortransmis à l'événement afin d'obtenir la valeur animée actuelle avec la méthodegetAnimatedValue(). L'implémentation de cet écouteur est obligatoire si vous utilisezValueAnimator.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é de couleur 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 quesetAlpha()etsetTranslationX(), invalident correctement la vue. Vous n'avez donc pas besoin d'invalider la vue lorsque vous appelez ces méthodes avec de nouvelles valeurs.
-
Vous pouvez étendre la classe AnimatorListenerAdapter au lieu d'implémenter l'interface Animator.AnimatorListener si vous ne souhaitez pas implémenter toutes les méthodes de 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
pour le rappel onAnimationEnd()
uniquement :
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 apportées aux objets ViewGroup
Le système d'animation de 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 à l'intérieur d'un ViewGroup peuvent faire l'objet d'une animation d'apparition et de disparition lorsque vous les ajoutez à un ViewGroup ou les en supprimez, 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 dans leurs nouvelles positions 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 de l'animation qui s'exécute sur les éléments qui apparaissent dans le conteneur.CHANGE_APPEARING: indicateur de l'animation qui s'exécute sur les éléments qui changent en raison de l'apparition d'un nouvel élément dans le conteneur.DISAPPEARING: indicateur de l'animation qui s'exécute sur les éléments qui disparaissent du conteneur.CHANGE_DISAPPEARING: indicateur de l'animation qui s'exécute sur les é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 des transitions de votre mise en page ou simplement indiquer 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" />
Si vous définissez cet attribut sur "true", les vues ajoutées ou supprimées du ViewGroup, ainsi que les vues restantes dans le ViewGroup, sont automatiquement animées.
Animer les changements d'état de 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, appelant cette animation chaque fois que l'état de vue spécifié (tel que "appuyé" ou "sélectionné") change.
L'élément StateListAnimator peut être défini dans une ressource XML avec un élément racine <selector> et des éléments enfants <item> 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'animations de propriété.
Par exemple, le fichier suivant crée un animateur de liste d'état qui modifie l'échelle X et Y de la vue lorsqu'elle est appuyée :
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'état à une vue, ajoutez l'attribut
android:stateListAnimator comme suit :
<Button android:stateListAnimator="@xml/animate_scale" ... />
Désormais, les animations définies dans animate_scale.xml sont utilisées lorsque l'état de ce bouton change.
Vous pouvez également attribuer un animateur de liste d'état à une vue dans votre code à l'aide de la méthode AnimatorInflater.loadStateListAnimator(), puis attribuer l'animateur à votre vue avec la méthode View.setStateListAnimator().
Vous pouvez également lire une animation de drawable entre les changements d'état au lieu d'animer les propriétés de la vue, à 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 par le système Android sont int, float ou une couleur, qui sont compatibles avec les évaluateurs de type IntEvaluator, FloatEvaluator et ArgbEvaluator.
Il n'existe qu'une seule méthode à implémenter dans l'interface TypeEvaluator, à savoir 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 écoulée actuelle de l'animation (une valeur comprise entre 0 et 1), puis calcule 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 à tenir compte de l'interpolateur lorsque vous calculez les valeurs animées.
Utiliser des interpolateurs
Un interpolateur définit la façon 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 produisent de manière linéaire sur l'ensemble de l'animation, ce qui signifie que l'animation se déplace de manière uniforme tout le temps. Vous pouvez également spécifier que les animations utilisent un temps non linéaire, par exemple en utilisant l'accélération ou la décélération au début ou à la fin de l'animation.
Les interpolateurs du système d'animation reçoivent une fraction des animateurs 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'ils visent à 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.
À titre d'exemple, la façon dont l'interpolateur par défaut AccelerateDecelerateInterpolator et LinearInterpolator calculent les fractions interpolées est comparée ci-dessous.
LinearInterpolator n'a aucun effet sur la fraction écoulée. AccelerateDecelerateInterpolator accélère l'animation et la ralentit. 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 qui dure 1 000 ms :
| ms écoulé | Fraction écoulée/Fraction interpolée (linéaire) | Fraction interpolée (accélérer/décélérer) |
|---|---|---|
| 0 | 0 | 0 |
| 200 | 0,2 | 0,1 |
| 400 | 0,4 | 0,345 |
| 600 | .6 | 0,654 |
| 800 | 0,8 | 0,9 |
| 1000 | 1 | 1 |
Comme le montre le tableau, LinearInterpolator modifie les valeurs à la même vitesse, soit 0,2 toutes 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 se compose d'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 l'heure de cette image clé.
Pour instancier un objet Keyframe, vous devez utiliser l'une des méthodes de fabrique, ofInt(), ofFloat() ou ofObject(), pour obtenir le type de Keyframe approprié. Vous appelez ensuite la méthode de fabrique ofKeyframe() pour obtenir un objet PropertyValuesHolder. Une fois que vous avez l'objet, 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 de propriété permet une animation simplifiée des objets View et offre quelques avantages par rapport au système d'animation de vue. Le système d'animation de vue transformait les objets View en modifiant la façon dont ils étaient dessinés. Cela était géré 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 entraînait des comportements tels qu'un objet existant toujours à son emplacement d'origine, même s'il était dessiné à un autre emplacement sur 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 de propriétés peut animer les vues à l'écran en modifiant les propriétés réelles dans les objets View. De plus, les vues appellent automatiquement la méthode invalidate() pour actualiser l'écran chaque fois que leurs propriétés sont modifiées. Voici les nouvelles propriétés de la classe View qui facilitent les animations de propriétés :
translationXettranslationY: 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,rotationXetrotationY: ces propriétés contrôlent la rotation en 2D (propriétérotation) et en 3D autour du point de pivot.scaleXetscaleY: ces propriétés contrôlent la mise à l'échelle 2D d'une vue autour de son point de pivot.pivotXetpivotY: ces propriétés contrôlent l'emplacement du point de pivot autour duquel les transformations de rotation et de mise à l'échelle se produisent. Par défaut, le point de pivot est situé au centre de l'objet.xety: il s'agit de propriétés utilitaires simples qui décrivent l'emplacement final de la vue dans son conteneur, en tant que somme des valeurs de gauche et du haut, ainsi que des valeurs de translationX et translationY.alpha: représente la transparence alpha de la vue. Cette valeur est définie sur 1 (opaque) par défaut. Une valeur de 0 représente 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. 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 sur l'animation avec ValueAnimator et ObjectAnimator.
Animer à l'aide de ViewPropertyAnimator
ViewPropertyAnimator offre un moyen simple d'animer plusieurs propriétés d'un 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 pour animer plusieurs propriétés à la fois. 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 entre 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();
Un 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 du blog des développeurs Android correspondant.
Déclarer des animations en XML
Le système d'animation de propriétés vous permet de déclarer des animations de propriétés avec XML au lieu de le faire par programmation. En définissant vos animations en XML, vous pouvez facilement les réutiliser dans plusieurs activités et modifier plus facilement la séquence d'animation.
Pour distinguer les fichiers d'animation qui utilisent les nouvelles API d'animation de propriété de ceux qui utilisent l'ancien framework d'animation de vue, à partir d'Android 3.1, vous devez enregistrer les fichiers XML pour les animations de propriété dans le répertoire res/animator/.
Les classes d'animation de propriété suivantes sont compatibles avec la déclaration XML à l'aide des balises XML suivantes :
ValueAnimator–<animator>ObjectAnimator–<objectAnimator>AnimatorSet–<set>
Pour trouver les attributs que vous pouvez utiliser dans votre déclaration XML, consultez 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 ensemble :
<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 en 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 le ValueAnimator précédent dans votre code, vous devez développer l'objet, ajouter un 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 Ressources d'animation .
Effets potentiels sur les performances de l'UI
Les animateurs qui mettent à jour l'UI entraînent un travail de rendu supplémentaire pour chaque frame dans lequel l'animation s'exécute. C'est pourquoi l'utilisation d'animations gourmandes en ressources peut avoir un impact négatif sur les performances de votre application.
Le travail nécessaire pour animer votre UI est ajouté à la phase d'animation du pipeline de rendu. Vous pouvez déterminer si vos animations ont un impact sur les performances de votre application en activant Profiler le rendu GPU et en surveillant l'étape d'animation. Pour en savoir plus, consultez Procédure pas à pas pour profiler le rendu GPU.