1. Avant de commencer
Cet atelier de programmation fait partie du cours Développement Android avancé en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre, mais ce n'est pas obligatoire. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Android avancé en Kotlin.
MotionLayout est une bibliothèque qui vous permet d'ajouter des animations riches à votre application Android. Elle est basée sur ConstraintLayout, et vous permet d'animer tout ce que vous pouvez créer à l'aide de ConstraintLayout.
Vous pouvez utiliser MotionLayout pour animer simultanément différents attributs pour plusieurs vues, parmi lesquels la position, la taille, la visibilité, la valeur alpha, la couleur, l'élévation et la rotation. À l'aide de fichiers XML déclaratifs, vous pouvez créer des animations coordonnées impliquant plusieurs vues qui sont difficiles à obtenir par codage.
Les animations sont un excellent moyen d'améliorer l'expérience d'une application. Vous pouvez utiliser des animations pour :
- Afficher les modifications : l'animation entre les états permet à l'utilisateur de suivre naturellement les modifications apportées à votre UI.
- Attirer l'attention : utilisez des animations pour attirer l'attention sur les éléments importants de l'UI.
- Créez des designs attrayants : des mouvements efficaces dans le design donnent aux applications une apparence soignée.
Prérequis
Cet atelier de programmation s'adresse aux développeurs ayant une certaine expérience du développement Android. Avant de commencer cet atelier de programmation, vous devez :
- Vous savez comment créer une application avec une activité et une mise en page de base, et l'exécuter sur un appareil ou un émulateur à l'aide d'Android Studio. Familiarisez-vous avec
ConstraintLayout. Pour en savoir plus surConstraintLayout, consultez l'atelier de programmation sur ConstraintLayout.
Objectifs de l'atelier
- Définir une animation avec
ConstraintSetsetMotionLayout - Animer en fonction des événements de déplacement
- Modifier l'animation avec
KeyPosition - Modifier les attributs avec
KeyAttribute - Exécuter des animations avec du code
- Animer les en-têtes réductibles avec
MotionLayout
Prérequis
- Android Studio 4.0 (l'éditeur
MotionLayoutne fonctionne qu'avec cette version d'Android Studio)
2. Premiers pas
Pour télécharger l'application exemple, vous pouvez :
Vous pouvez également cloner le dépôt GitHub à partir de la ligne de commande à l'aide de la commande suivante :
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. Créer des animations avec MotionLayout
Vous allez d'abord créer une animation qui déplace une vue du haut de l'écran vers le bas en réponse aux clics de l'utilisateur.
Pour créer une animation à partir du code de démarrage, vous aurez besoin des éléments principaux suivants :
- Un
MotionLayout,qui est une sous-classe deConstraintLayout. Vous spécifiez toutes les vues à animer dans la baliseMotionLayout. - Un
MotionScene,, qui est un fichier XML décrivant une animation pourMotionLayout. - Un
Transition,qui fait partie duMotionSceneet qui spécifie la durée de l'animation, le déclencheur et la façon de déplacer les vues. ConstraintSetqui spécifie les contraintes start et end de la transition.
Examinons chacun d'eux, en commençant par MotionLayout.
Étape 1 : Explorer le code existant
MotionLayout est une sous-classe de ConstraintLayout. Elle est donc compatible avec toutes les mêmes fonctionnalités tout en ajoutant une animation. Pour utiliser MotionLayout, vous devez ajouter une vue MotionLayout à l'endroit où vous utiliseriez ConstraintLayout..
- Dans
res/layout, ouvrezactivity_step1.xml.. Vous y trouverez unConstraintLayoutavec un seulImageViewd'étoile, avec une teinte appliquée à l'intérieur.
activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Cette ConstraintLayout n'a aucune contrainte. Par conséquent, si vous exécutez l'application maintenant, vous verrez l'étoile s'afficher sans contrainte, ce qui signifie qu'elle sera positionnée à un emplacement inconnu. Android Studio vous avertit de l'absence de contraintes.
Étape 2 : Convertissez en Motion Layout
Pour animer à l'aide de MotionLayout,, vous devez convertir le ConstraintLayout en MotionLayout.
Pour que votre mise en page utilise une scène de mouvement, elle doit y faire référence.
- Pour ce faire, ouvrez la surface de conception. Dans Android Studio 4.0, vous ouvrez la surface de conception en utilisant l'icône de division ou de conception en haut à droite lorsque vous consultez un fichier XML de mise en page.

- Une fois la surface de conception ouverte, effectuez un clic droit sur l'aperçu et sélectionnez Convert to MotionLayout (Convertir en MotionLayout).

Cela remplace la balise ConstraintLayout par une balise MotionLayout et ajoute un motion:layoutDescription à la balise MotionLayout qui pointe vers @xml/activity_step1_scene..
activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
Une scène de mouvement est un fichier XML unique qui décrit une animation dans un MotionLayout.
Dès que vous convertissez un MotionLayout, l'Éditeur de mouvement s'affiche sur la surface de conception.

L'Éditeur de mouvement comporte trois nouveaux éléments d'UI :
- Aperçu : il s'agit d'une sélection modale qui vous permet de choisir différentes parties de l'animation. Dans cette image, le
ConstraintSetstartest sélectionné. Vous pouvez également sélectionner la transition entrestartetenden cliquant sur la flèche entre les deux. - Section : sous l'aperçu se trouve une fenêtre de section qui change en fonction de l'élément d'aperçu actuellement sélectionné. Dans cette image, les informations
startConstraintSets'affichent dans la fenêtre de sélection. - Attribut : le panneau d'attributs affiche et vous permet de modifier les attributs de l'élément actuellement sélectionné dans la fenêtre "Vue d'ensemble" ou "Sélection". Cette image montre les attributs de l'
ConstraintSetstart.
Étape 3 : Définissez les contraintes de début et de fin
Toutes les animations peuvent être définies en termes de début et de fin. Le début décrit l'apparence de l'écran avant l'animation, et la fin décrit l'apparence de l'écran une fois l'animation terminée. MotionLayout est responsable de la détermination de la manière d'animer entre l'état de début et l'état de fin (au fil du temps).
MotionScene utilise une balise ConstraintSet pour définir les états de début et de fin. Un ConstraintSet est un ensemble de contraintes qui peuvent être appliquées aux vues. Cela inclut les contraintes de largeur, de hauteur et ConstraintLayout. Il inclut également certains attributs tels que alpha. Il ne contient pas les vues elles-mêmes, mais uniquement les contraintes qui s'y appliquent.
Toutes les contraintes spécifiées dans un fichier ConstraintSet remplaceront celles spécifiées dans le fichier de mise en page. Si vous définissez des contraintes à la fois dans la mise en page et dans MotionScene, seules les contraintes de MotionScene sont appliquées.
Dans cette étape, vous allez contraindre la vue en étoile à commencer en haut à gauche de l'écran et à se terminer en bas à droite.
Vous pouvez effectuer cette étape à l'aide de l'éditeur de mouvement ou en modifiant directement le texte de activity_step1_scene.xml.
- Sélectionnez le
startConstraintSet dans le panneau "Vue d'ensemble".

- Dans le panneau Sélection, sélectionnez
red_star. Actuellement, la sourcelayouts'affiche, ce qui signifie qu'elle n'est pas contrainte dans ceConstraintSet. Utilisez l'icône en forme de crayon en haut à droite pour créer une contrainte.

- Vérifiez que
red_staraffiche une sourcestartlorsque l'startConstraintSetest sélectionné dans le panneau "Vue d'ensemble". - Dans le panneau "Attributes" (Attributs), avec
red_starsélectionné dansstartConstraintSet, ajoutez une contrainte en haut et commencez par cliquer sur les boutons bleus +.

- Ouvrez
xml/activity_step1_scene.xmlpour afficher le code généré par l'éditeur de mouvements pour cette contrainte.
activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
ConstraintSet a une id de @id/start et spécifie toutes les contraintes à appliquer à toutes les vues de MotionLayout. Étant donné que ce MotionLayout ne comporte qu'une seule vue, il n'a besoin que d'un seul Constraint.
Le Constraint à l'intérieur de ConstraintSet spécifie l'ID de la vue qu'il contraint, @id/red_star défini dans activity_step1.xml. Il est important de noter que les balises Constraint ne spécifient que les contraintes et les informations de mise en page. La balise Constraint ne sait pas qu'elle est appliquée à un ImageView.
Cette contrainte spécifie la hauteur, la largeur et les deux autres contraintes nécessaires pour contraindre la vue red_star au début supérieur de son parent.
- Sélectionnez le
endConstraintSet dans le panneau "Vue d'ensemble".

- Suivez les mêmes étapes que précédemment pour ajouter un
Constraintpourred_stardans leConstraintSetend. - Pour utiliser l'éditeur de mouvement pour effectuer cette étape, ajoutez une contrainte aux
bottometenden cliquant sur les boutons bleus +.

- Le code XML se présente comme suit :
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
Comme @id/start, ce ConstraintSet comporte un seul Constraint sur @id/red_star. Cette fois, il le contraint à la partie inférieure de l'écran.
Vous n'êtes pas obligé de les nommer @id/start et @id/end, mais c'est pratique.
Étape 4 : Définissez une transition
Chaque MotionScene doit également inclure au moins une transition. Une transition définit chaque partie d'une animation, du début à la fin.
Une transition doit spécifier un ConstraintSet de début et de fin. Une transition peut également spécifier d'autres façons de modifier l'animation, par exemple la durée d'exécution de l'animation ou la façon d'animer en faisant glisser des vues.
- Motion Editor a créé une transition par défaut lorsque le fichier MotionScene a été créé. Ouvrez
activity_step1_scene.xmlpour afficher la transition générée.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
C'est tout ce dont MotionLayout a besoin pour créer une animation. Examinons chaque attribut :
constraintSetStartsera appliqué aux vues au début de l'animation.constraintSetEndsera appliqué aux vues à la fin de l'animation.durationindique la durée de l'animation en millisecondes.
MotionLayout trouvera ensuite un chemin entre les contraintes de début et de fin, et l'animera pendant la durée spécifiée.
Étape 5 : Prévisualisez l'animation dans l'éditeur de mouvement

Animation : vidéo montrant la prévisualisation d'une transition dans l'éditeur de mouvements
- Ouvrez l'éditeur de mouvement et sélectionnez la transition en cliquant sur la flèche entre
startetenddans le panneau "Vue d'ensemble".

- Le panneau Sélection affiche les commandes de lecture et une barre de défilement lorsqu'une transition est sélectionnée. Cliquez sur le bouton de lecture ou faites glisser la position actuelle pour prévisualiser l'animation.

Étape 6 : Ajoutez un gestionnaire de clics
Vous avez besoin d'un moyen de démarrer l'animation. Pour ce faire, vous pouvez configurer MotionLayout pour qu'il réponde aux événements de clic sur @id/red_star.
- Ouvrez l'éditeur de mouvement et sélectionnez la transition en cliquant sur la flèche entre le début et la fin dans le panneau "Vue d'ensemble".

- Cliquez sur
Créer un gestionnaire de clics ou de balayages dans la barre d'outils du panneau "Aperçu". Cela ajoute un gestionnaire qui lancera une transition. - Sélectionnez Click Handler (Gestionnaire de clics) dans le pop-up.

- Définissez Vue à clic sur
red_star.

- Cliquez sur Ajouter. Le gestionnaire de clics est représenté par un petit point dans l'éditeur de mouvement de la transition.

- Sélectionnez la transition dans le panneau "Aperçu", puis ajoutez un attribut
clickActiondetoggleau gestionnaire OnClick que vous venez d'ajouter dans le panneau "Attributs".

- Ouvrez
activity_step1_scene.xmlpour afficher le code généré par l'éditeur de mouvements.
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
Transition indique à MotionLayout d'exécuter l'animation en réponse aux événements de clic à l'aide d'une balise <OnClick>. Examinons chaque attribut :
targetIdest la vue à surveiller pour les clics.clickActiondetogglebascule entre l'état de début et l'état de fin au clic. Vous trouverez d'autres options pourclickActiondans la documentation.
- Exécutez votre code, cliquez sur Étape 1, puis sur l'étoile rouge pour voir l'animation !
Étape 5 : Les animations en action
Exécutez l'application ! Vous devriez voir votre animation s'exécuter lorsque vous cliquez sur l'étoile.

Le fichier de scène de mouvement terminé définit un Transition qui pointe vers un ConstraintSet de début et de fin.
Au début de l'animation (@id/start), l'icône en forme d'étoile est contrainte au début du haut de l'écran. À la fin de l'animation (@id/end), l'icône en forme d'étoile est contrainte à la fin inférieure de l'écran.
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. Animer en fonction des événements de déplacement
Pour cette étape, vous allez créer une animation qui répond à un événement de déplacement de l'utilisateur (lorsque l'utilisateur balaye l'écran) pour exécuter l'animation. MotionLayout permet de suivre les événements tactiles pour déplacer les vues, ainsi que les gestes de balayage basés sur la physique pour rendre le mouvement fluide.
Étape 1 : Inspecter le code initial
- Pour commencer, ouvrez le fichier de mise en page
activity_step2.xml, qui contient unMotionLayoutexistant. Examinons le code.
activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Cette mise en page définit toutes les vues de l'animation. Les trois icônes en forme d'étoile ne sont pas contraintes dans la mise en page, car elles seront animées dans la scène de mouvement.
Des contraintes sont appliquées aux crédits TextView, car ils restent au même endroit pendant toute l'animation et ne modifient aucun attribut.
Étape 2 : Animer la scène
Comme pour la dernière animation, l'animation sera définie par un ConstraintSet, de début et de fin, et un Transition.
Définissez le ConstraintSet de début.
- Ouvrez la scène de mouvement
xml/step2.xmlpour définir l'animation. - Ajoutez les contraintes pour la contrainte de départ
start. Au début, les trois étoiles sont centrées en bas de l'écran. Les étoiles de droite et de gauche ont une valeuralphade0.0, ce qui signifie qu'elles sont totalement transparentes et masquées.
step2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
Dans ce ConstraintSet, vous spécifiez un Constraint pour chacune des étoiles. Chaque contrainte sera appliquée par MotionLayout au début de l'animation.
Chaque vue d'étoile est centrée en bas de l'écran à l'aide de contraintes de début, de fin et de bas. Les deux étoiles @id/left_star et @id/right_star ont une valeur alpha supplémentaire qui les rend invisibles et qui sera appliquée au début de l'animation.
Les ensembles de contraintes start et end définissent le début et la fin de l'animation. Une contrainte sur le début, comme motion:layout_constraintStart_toStartOf, contraindra le début d'une vue à celui d'une autre vue. Cela peut être déroutant au premier abord, car le nom start est utilisé à la fois pour et, et les deux sont utilisés dans le contexte des contraintes. Pour mieux comprendre la différence, le start dans layout_constraintStart fait référence au "début" de la vue, qui est à gauche dans une langue qui se lit de gauche à droite et à droite dans une langue qui se lit de droite à gauche. L'ensemble de contraintes start fait référence au début de l'animation.
Définir le ConstraintSet de fin
- Définissez la contrainte de fin pour utiliser une chaîne afin de positionner les trois étoiles ensemble sous
@id/credits. De plus, il définira la valeur de fin dealphades étoiles de gauche et de droite sur1.0.
step2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
Le résultat final est que les vues s'étalent et s'élèvent à partir du centre lors de l'animation.
De plus, comme la propriété alpha est définie sur @id/right_start et @id/left_star dans ConstraintSets, les deux vues s'afficheront progressivement au fur et à mesure de l'animation.
Animer en fonction du balayage de l'utilisateur
MotionLayout peut suivre les événements de déplacement de l'utilisateur ou un balayage pour créer une animation de "lancer" basée sur la physique. Cela signifie que les vues continueront à défiler si l'utilisateur les fait glisser et ralentiront comme un objet physique qui roule sur une surface. Vous pouvez ajouter ce type d'animation avec une balise OnSwipe dans Transition.
- Remplacez le TODO pour ajouter un tag
OnSwipepar<OnSwipe motion:touchAnchorId="@id/red_star" />.
step2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
OnSwipe contient quelques attributs, dont le plus important est touchAnchorId.
touchAnchorIdest la vue suivie qui se déplace en réponse au toucher.MotionLayoutconservera cette vue à la même distance du doigt qui effectue le balayage.touchAnchorSidedétermine le côté de la vue à suivre. Cela est important pour les vues qui sont redimensionnées, qui suivent des chemins complexes ou dont un côté se déplace plus rapidement que l'autre.dragDirectiondétermine la direction qui compte pour cette animation (vers le haut, le bas, la gauche ou la droite).
Lorsque MotionLayout écoute les événements de déplacement, l'écouteur est enregistré sur la vue MotionLayout et non sur celle spécifiée par touchAnchorId. Lorsqu'un utilisateur commence un geste n'importe où sur l'écran, MotionLayout maintient constante la distance entre son doigt et le touchAnchorSide de la vue touchAnchorId. Par exemple, s'ils appuient à 100 dp de la bordure d'ancrage, MotionLayout maintiendra cette bordure à 100 dp de leur doigt pendant toute l'animation.
Essayer
- Exécutez de nouveau l'application et ouvrez l'écran de l'étape 2. L'animation s'affiche.
- Essayez de faire glisser votre doigt ou de le relâcher à mi-chemin de l'animation pour découvrir comment
MotionLayoutaffiche des animations fluides basées sur la physique.

MotionLayout peut animer des designs très différents à l'aide des fonctionnalités de ConstraintLayout pour créer des effets riches.
Dans cette animation, les trois vues sont positionnées par rapport à leur parent en bas de l'écran au début. À la fin, les trois vues sont positionnées par rapport à @id/credits dans une chaîne.
Malgré ces mises en page très différentes, MotionLayout créera une animation fluide entre le début et la fin.
5. Modifier un chemin d'accès
Dans cette étape, vous allez créer une animation qui suit un chemin complexe et anime les crédits pendant le mouvement. MotionLayout peut modifier le chemin qu'une vue empruntera entre le début et la fin à l'aide d'un KeyPosition.
Étape 1 : Explorez le code existant
- Ouvrez
layout/activity_step3.xmletxml/step3.xmlpour afficher la mise en page et la scène de mouvement existantes. LesImageViewetTextViewaffichent la lune et le texte des crédits. - Ouvrez le fichier de scène de mouvement (
xml/step3.xml). Vous voyez qu'unTransitionde@id/startà@id/endest défini. L'animation déplace l'image de la lune du bas à gauche de l'écran vers le bas à droite de l'écran à l'aide de deuxConstraintSets. Le texte des crédits apparaît progressivement dealpha="0.0"àalpha="1.0"à mesure que la lune se déplace. - Exécutez l'application maintenant et sélectionnez Étape 3. Vous verrez que la Lune suit une trajectoire linéaire (ou une ligne droite) du début à la fin lorsque vous cliquez dessus.
Étape 2 : Activez le débogage des chemins
Avant d'ajouter un arc au mouvement de la lune, il est utile d'activer le débogage du chemin dans MotionLayout.
Pour vous aider à développer des animations complexes avec MotionLayout, vous pouvez dessiner le chemin d'animation de chaque vue. Cela est utile lorsque vous souhaitez visualiser votre animation et affiner les petits détails du mouvement.
- Pour activer les chemins de débogage, ouvrez
layout/activity_step3.xmlet ajoutezmotion:motionDebug="SHOW_PATH"à la baliseMotionLayout.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
Une fois le débogage de chemin activé, lorsque vous exécutez à nouveau l'application, les chemins de toutes les vues sont visualisés avec une ligne en pointillés.

- Les cercles représentent la position de début ou de fin d'une vue.
- Les lignes représentent le chemin d'une vue.
- Les losanges représentent un
KeyPositionqui modifie le chemin.
Par exemple, dans cette animation, le cercle du milieu correspond à la position du texte des crédits.
Étape 3 : Modifiez un chemin d'accès
Toutes les animations de MotionLayout sont définies par un ConstraintSet de début et de fin qui définit l'apparence de l'écran avant le début de l'animation et après sa fin. Par défaut, MotionLayout trace un chemin linéaire (une ligne droite) entre la position de départ et la position de fin de chaque vue dont la position change.
Pour créer des chemins complexes comme l'arc de la lune dans cet exemple, MotionLayout utilise un KeyPosition pour modifier le chemin emprunté par une vue entre le début et la fin.
- Ouvrez
xml/step3.xmlet ajoutez unKeyPositionà la scène. La baliseKeyPositionest placée à l'intérieur de la baliseTransition.

step3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Un KeyFrameSet est un enfant d'un Transition. Il s'agit d'un ensemble de tous les KeyFrames, tels que KeyPosition, qui doivent être appliqués pendant la transition.
Comme MotionLayout calcule le chemin de la lune entre le début et la fin, il modifie le chemin en fonction de KeyPosition spécifié dans KeyFrameSet. Vous pouvez voir comment cela modifie le chemin en exécutant à nouveau l'application.
Un KeyPosition possède plusieurs attributs qui décrivent la façon dont il modifie le chemin d'accès. Voici les plus importants :
framePositionest un nombre compris entre 0 et 100. Il définit le moment où ceKeyPositiondoit être appliqué dans l'animation, 1 correspondant à 1 % de l'animation et 99 à 99 %. Si la valeur est de 50, vous l'appliquez au milieu.motionTargetest la vue pour laquelle ceKeyPositionmodifie le chemin.keyPositionTypeindique commentKeyPositionmodifie le chemin d'accès. Il peut s'agir deparentRelative,pathRelativeoudeltaRelative(comme expliqué à l'étape suivante).percentX | percentYcorrespond à la valeur de modification du chemin àframePosition(valeurs comprises entre 0 et 1, avec des valeurs négatives et des valeurs supérieures à 1 autorisées).
Vous pouvez le voir comme suit : "À framePosition, modifiez le chemin d'accès de motionTarget en le déplaçant de percentX ou percentY selon les coordonnées déterminées par keyPositionType."
Par défaut, MotionLayout arrondit tous les angles créés en modifiant le chemin d'accès. Si vous regardez l'animation que vous venez de créer, vous pouvez voir que la lune suit une trajectoire courbe au niveau de l'angle. Dans la plupart des cas, c'est ce que vous souhaitez. Si ce n'est pas le cas, vous pouvez spécifier l'attribut curveFit pour le personnaliser.
Essayer
Si vous exécutez à nouveau l'application, vous verrez l'animation pour cette étape.

La lune suit un arc, car elle passe par un KeyPosition spécifié dans Transition.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
Vous pouvez lire ce KeyPosition comme suit : "À framePosition 50 (à mi-chemin de l'animation), modifiez le chemin d'accès de motionTarget @id/moon en le déplaçant de 50% Y (à mi-hauteur de l'écran) selon les coordonnées déterminées par parentRelative (l'ensemble de MotionLayout)."
Ainsi, à mi-chemin de l'animation, la lune doit passer par un KeyPosition qui se trouve à 50 % en bas de l'écran. Ce KeyPosition ne modifie en rien le mouvement X. La lune ira donc toujours d'un bout à l'autre horizontalement. MotionLayout trouvera un chemin fluide qui passe par ce KeyPosition tout en se déplaçant entre le début et la fin.
Si vous regardez attentivement, le texte des crédits est contraint par la position de la lune. Pourquoi ne se déplace-t-il pas aussi verticalement ?

<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
Il s'avère que même si vous modifiez la trajectoire de la Lune, ses positions de départ et d'arrivée ne la déplacent pas du tout verticalement. KeyPosition ne modifie pas la position de début ni celle de fin. Le texte des crédits est donc limité à la position de fin de la lune.
Si vous souhaitez que les crédits se déplacent avec la lune, vous pouvez ajouter un KeyPosition aux crédits ou modifier les contraintes de début sur @id/credits.
Dans la section suivante, vous allez découvrir les différents types de keyPositionType dans MotionLayout.
6. Comprendre keyPositionType
À la dernière étape, vous avez utilisé un parentRelative de type keyPosition pour décaler le chemin de 50 % de l'écran. L'attribut keyPositionType détermine comment MotionLayout modifiera le chemin en fonction de percentX ou percentY.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Il existe trois types de keyPosition : parentRelative, pathRelative et deltaRelative. Si vous spécifiez un type, le système de coordonnées utilisé pour calculer percentX et percentY sera modifié.
Qu'est-ce qu'un système de coordonnées ?
Un système de coordonnées permet de spécifier un point dans l'espace. Elles sont également utiles pour décrire une position à l'écran.
Les systèmes de coordonnées MotionLayout sont des systèmes de coordonnées cartésiennes. Cela signifie qu'ils ont un axe X et un axe Y définis par deux lignes perpendiculaires. La principale différence entre eux réside dans la position de l'axe X sur l'écran (l'axe Y est toujours perpendiculaire à l'axe X).
Tous les systèmes de coordonnées de MotionLayout utilisent des valeurs comprises entre 0.0 et 1.0 sur les axes X et Y. Elles autorisent les valeurs négatives et les valeurs supérieures à 1.0. Par exemple, une valeur percentX de -2.0 signifie "aller dans la direction opposée de l'axe X deux fois".
Si tout cela vous rappelle un peu trop vos cours d'algèbre, consultez les images ci-dessous.
Coordonnées parentRelative

Le keyPositionType de parentRelative utilise le même système de coordonnées que l'écran. Il définit (0, 0) en haut à gauche de l'ensemble de MotionLayout et (1, 1) en bas à droite.
Vous pouvez utiliser parentRelative chaque fois que vous souhaitez créer une animation qui se déplace sur l'ensemble de MotionLayout, comme l'arc de lune dans cet exemple.
Toutefois, si vous souhaitez modifier un chemin par rapport au mouvement, par exemple pour le rendre légèrement incurvé, les deux autres systèmes de coordonnées sont plus adaptés.
Coordonnées delta relatives

Delta est un terme mathématique qui signifie "variation". deltaRelative signifie donc "variation relative". Dans les coordonnées deltaRelative, (0,0) correspond à la position de départ de la vue et (1,1) à la position de fin. Les axes X et Y sont alignés sur l'écran.
L'axe X est toujours horizontal sur l'écran, et l'axe Y est toujours vertical. Par rapport à parentRelative, la principale différence est que les coordonnées décrivent uniquement la partie de l'écran dans laquelle la vue se déplacera.
deltaRelative est un excellent système de coordonnées pour contrôler le mouvement horizontal ou vertical de manière isolée. Par exemple, vous pouvez créer une animation qui ne termine son mouvement vertical (Y) qu'à 50 % et continue de s'animer horizontalement (X).
pathRelative coordinates

Le dernier système de coordonnées dans MotionLayout est pathRelative. Elle est très différente des deux autres, car l'axe X suit la trajectoire d'animation du début à la fin. (0,0) est la position de départ et (1,0) est la position de fin.
Pourquoi faire cela ? Cela peut paraître surprenant au premier abord, d'autant plus que ce système de coordonnées n'est même pas aligné sur le système de coordonnées de l'écran.
Il s'avère que pathRelative est très utile pour plusieurs choses.
- Accélérer, ralentir ou arrêter une vue pendant une partie de l'animation Étant donné que la dimension X correspond toujours exactement au chemin emprunté par la vue, vous pouvez utiliser un
pathRelativeKeyPositionpour modifier leframePositionauquel un point spécifique de ce chemin est atteint. Ainsi, unKeyPositionàframePosition="50"avec unpercentX="0.1"fera que l'animation prendra 50 % du temps pour parcourir les 10 % du mouvement. - Ajouter un arc subtil à un chemin d'accès. Étant donné que la dimension Y est toujours perpendiculaire au mouvement, la modification de Y entraînera une modification de la trajectoire, qui deviendra courbe par rapport au mouvement global.
- Vous ne pouvez pas ajouter de deuxième dimension lorsque
deltaRelativeest sélectionné. Pour un mouvement complètement horizontal ou vertical,deltaRelativene créera qu'une seule dimension utile. Toutefois,pathRelativecréera toujours des coordonnées X et Y utilisables.
Dans l'étape suivante, vous allez apprendre à créer des chemins encore plus complexes en utilisant plusieurs KeyPosition.
7. Créer des chemins complexes
En regardant l'animation que vous avez créée à l'étape précédente, vous constatez qu'elle crée une courbe lisse, mais que la forme pourrait être plus "lunaire".
Modifier un chemin comportant plusieurs éléments KeyPosition
MotionLayout peut modifier davantage un chemin en définissant autant de KeyPosition que nécessaire pour obtenir n'importe quel mouvement. Pour cette animation, vous allez créer un arc, mais vous pourriez faire sauter la lune de haut en bas au milieu de l'écran, si vous le souhaitez.
- Ouvrez
xml/step4.xml. Vous pouvez constater qu'il comporte les mêmes vues et leKeyFrameque vous avez ajouté à la dernière étape. - Pour arrondir le haut de la courbe, ajoutez deux autres
KeyPositionsau chemin de@id/moon, l'un juste avant qu'il n'atteigne le sommet et l'autre après.

step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
Ces KeyPositions seront appliqués à 25 % et 75 % de l'animation, et feront passer @id/moon par un chemin situé à 60 % du haut de l'écran. Combiné à la KeyPosition existante à 50 %, cela crée un arc lisse que la lune peut suivre.
Dans MotionLayout, vous pouvez ajouter autant de KeyPositions que nécessaire pour obtenir la trajectoire souhaitée. MotionLayout appliquera chaque KeyPosition à la framePosition spécifiée et trouvera comment créer un mouvement fluide qui passe par tous les KeyPositions.
Essayer
- Exécutez à nouveau l'application. Accédez à l'étape 4 pour voir l'animation en action. Lorsque vous cliquez sur la lune, elle suit le chemin du début à la fin, en passant par chaque
KeyPositionspécifié dans leKeyFrameSet.
Explorer par vous-même
Avant de passer à d'autres types de KeyFrame, essayez d'ajouter d'autres KeyPositions à KeyFrameSet pour voir quels types d'effets vous pouvez créer en utilisant uniquement KeyPosition.
Voici un exemple montrant comment créer un chemin complexe qui se déplace d'avant en arrière pendant l'animation.

step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
Une fois que vous avez terminé d'explorer KeyPosition, vous passerez à d'autres types de KeyFrames à l'étape suivante.
8. Modifier les attributs pendant le mouvement
Pour créer des animations dynamiques, il faut souvent modifier les size, rotation ou alpha des vues à mesure que l'animation progresse. MotionLayout permet d'animer de nombreux attributs sur n'importe quelle vue à l'aide d'un KeyAttribute.
Dans cette étape, vous allez utiliser KeyAttribute pour mettre à l'échelle et faire pivoter la lune. Vous utiliserez également KeyAttribute pour retarder l'apparition du texte jusqu'à ce que la lune ait presque terminé son voyage.
Étape 1 : Redimensionnez et faites pivoter avec KeyAttribute
- Ouvrez
xml/step5.xml, qui contient la même animation que celle que vous avez créée à l'étape précédente. Pour varier les plaisirs, cet écran utilise une autre image de l'espace comme arrière-plan. - Pour que la lune grandisse et tourne, ajoutez deux balises
KeyAttributedansKeyFrameSetàkeyFrame="50"etkeyFrame="100".

step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
Ces KeyAttributes sont appliqués à 50 % et 100 % de l'animation. Le premier KeyAttribute à 50 % se produit en haut de l'arc et entraîne un doublement de la taille de la vue, ainsi qu'une rotation de -360 degrés (ou un cercle complet). Le deuxième KeyAttribute terminera la deuxième rotation à -720 degrés (deux cercles complets) et réduira la taille à la normale, car les valeurs scaleX et scaleY sont définies par défaut sur 1.0.
Comme un KeyPosition, un KeyAttribute utilise framePosition et motionTarget pour spécifier quand appliquer le KeyFrame et quelle vue modifier. MotionLayout effectuera une interpolation entre KeyPositions pour créer des animations fluides.
KeyAttributes accepte les attributs qui peuvent être appliqués à toutes les vues. Ils permettent de modifier des attributs de base tels que visibility, alpha ou elevation. Vous pouvez également modifier la rotation comme vous le faites ici, faire pivoter en trois dimensions avec rotateX et rotateY, mettre à l'échelle la taille avec scaleX et scaleY, ou traduire la position de la vue en X, Y ou Z.
Étape 2 : Retardez l'affichage des crédits
L'un des objectifs de cette étape est de mettre à jour l'animation afin que le texte des crédits n'apparaisse qu'une fois l'animation presque terminée.
- Pour retarder l'apparition des crédits, définissez un autre
KeyAttributequi garantit quealpharestera à 0 jusqu'àkeyPosition="85".MotionLayoutpassera toujours en douceur de 0 à 100 alpha, mais sur les 15 derniers pour cent de l'animation.
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
Ce KeyAttribute maintient le alpha de @id/credits à 0,0 pour les 85 premiers pour cent de l'animation. Comme il commence avec un canal alpha de 0, cela signifie qu'il sera invisible pendant les 85 premiers pour cent de l'animation.
L'effet final de ce KeyAttribute est que les crédits apparaissent vers la fin de l'animation. Cela donne l'impression qu'ils sont coordonnés avec la lune qui se couche dans l'angle droit de l'écran.
En retardant les animations sur une vue pendant qu'une autre vue se déplace de cette manière, vous pouvez créer des animations impressionnantes qui semblent dynamiques pour l'utilisateur.
Essayer
- Exécutez à nouveau l'application et passez à l'étape 5 pour voir l'animation en action. Lorsque vous cliquez sur la lune, elle suit le chemin du début à la fin, en passant par chaque
KeyAttributespécifié dans leKeyFrameSet.

Comme vous faites faire à la lune deux tours complets, elle effectue un double salto arrière et les crédits n'apparaissent qu'à la fin de l'animation.
Explorer par vous-même
Avant de passer au dernier type de KeyFrame, essayez de modifier d'autres attributs standards dans KeyAttributes. Par exemple, essayez de remplacer rotation par rotationX pour voir l'animation produite.
Voici une liste des attributs standards que vous pouvez essayer :
android:visibilityandroid:alphaandroid:elevationandroid:rotationandroid:rotationXandroid:rotationYandroid:scaleXandroid:scaleYandroid:translationXandroid:translationYandroid:translationZ
9. Modifier les attributs personnalisés
Les animations riches consistent à modifier la couleur ou d'autres attributs d'une vue. Alors que MotionLayout peut utiliser un KeyAttribute pour modifier l'un des attributs standards listés dans la tâche précédente, vous utilisez un CustomAttribute pour spécifier tout autre attribut.
Un CustomAttribute peut être utilisé pour définir n'importe quelle valeur disposant d'un setter. Par exemple, vous pouvez définir backgroundColor sur une vue à l'aide d'un CustomAttribute. MotionLayout utilisera la réflexion pour trouver le setter, puis l'appellera à plusieurs reprises pour animer la vue.
Au cours de cette étape, vous allez utiliser CustomAttribute pour définir l'attribut colorFilter sur la lune afin de créer l'animation ci-dessous.

Définir des attributs personnalisés
- Pour commencer, ouvrez
xml/step6.xml, qui contient la même animation que celle que vous avez créée à l'étape précédente. - Pour que la lune change de couleur, ajoutez deux
KeyAttributeavec unCustomAttributedans leKeyFrameSetàkeyFrame="0",keyFrame="50"etkeyFrame="100"..

step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
Vous ajoutez un CustomAttribute dans un KeyAttribute. Le CustomAttribute sera appliqué au framePosition spécifié par KeyAttribute.
Dans CustomAttribute, vous devez spécifier un attributeName et une valeur à définir.
motion:attributeNameest le nom du setter qui sera appelé par cet attribut personnalisé. Dans cet exemple,setColorFiltersurDrawablesera appelé.motion:custom*Valueest une valeur personnalisée du type indiqué dans le nom. Dans cet exemple, la valeur personnalisée est une couleur spécifiée.
Les valeurs personnalisées peuvent être de l'un des types suivants :
- Couleur
- Nombre entier
- Float
- Chaîne
- Dimension
- Booléen
Grâce à cette API, MotionLayout peut animer tout élément qui fournit un setter sur une vue.
Essayer
- Exécutez de nouveau l'application et passez à l'étape 6 pour voir l'animation en action. Lorsque vous cliquez sur la lune, elle suit le chemin du début à la fin, en passant par chaque
KeyAttributespécifié dans leKeyFrameSet.

Lorsque vous ajoutez d'autres KeyFrames, MotionLayout modifie la trajectoire de la lune, qui passe d'une ligne droite à une courbe complexe, en ajoutant un double salto arrière, un redimensionnement et un changement de couleur à mi-parcours de l'animation.
Dans les animations réelles, vous animez souvent plusieurs vues en même temps, en contrôlant leur mouvement selon différents chemins et vitesses. En spécifiant un KeyFrame différent pour chaque vue, il est possible de chorégraphier des animations riches qui animent plusieurs vues avec MotionLayout.
10. Événements de déplacement et chemins complexes
Dans cette étape, vous allez découvrir comment utiliser OnSwipe avec des chemins complexes. Jusqu'à présent, l'animation de la lune était déclenchée par un écouteur OnClick et s'exécutait pendant une durée fixe.
Pour contrôler les animations dont les chemins sont complexes à l'aide de OnSwipe, comme l'animation de la lune que vous avez créée au cours des dernières étapes, vous devez comprendre comment fonctionne OnSwipe.
Étape 1 : Explorer le comportement OnSwipe
- Ouvrez
xml/step7.xmlet recherchez la déclarationOnSwipeexistante.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- Exécutez l'application sur votre appareil et passez à l'étape 7. Voyez si vous pouvez créer une animation fluide en faisant glisser la lune le long de l'arc.
Lorsque vous exécutez cette animation, le résultat n'est pas très satisfaisant. Une fois que la lune atteint le sommet de l'arc, elle commence à sauter.

Pour comprendre le bug, imaginez ce qui se passe lorsque l'utilisateur appuie juste en dessous du haut de l'arc. Étant donné que la balise OnSwipe comporte un motion:touchAnchorSide="bottom", MotionLayout tentera de maintenir la distance entre le doigt et le bas de la vue constante tout au long de l'animation.
Toutefois, comme le bas de la lune ne va pas toujours dans la même direction (il monte, puis redescend), MotionLayout ne sait pas quoi faire lorsque l'utilisateur vient de dépasser le haut de l'arc. Pour ce faire, puisque vous suivez le bas de la lune, où doit-il être placé lorsque l'utilisateur appuie ici ?

Étape 2 : Utilisez le côté droit
Pour éviter ce type de bug, il est important de toujours choisir un touchAnchorId et un touchAnchorSide qui progressent toujours dans une seule direction pendant toute la durée de l'animation.
Dans cette animation, les côtés right et left de la lune progressent à l'écran dans une même direction.
Toutefois, bottom et top inverseront leur direction. Lorsque OnSwipe tente de les suivre, il est dérouté lorsque leur direction change.
- Pour que cette animation suive les événements tactiles, remplacez
touchAnchorSideparright.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Étape 3 : Utilisez dragDirection
Vous pouvez également combiner dragDirection avec touchAnchorSide pour que la voie de service prenne une direction différente de celle qu'elle prendrait normalement. Il est toujours important que touchAnchorSide ne progresse que dans une seule direction, mais vous pouvez indiquer à MotionLayout la direction à suivre. Par exemple, vous pouvez conserver touchAnchorSide="bottom", mais ajouter dragDirection="dragRight". MotionLayout suivra alors la position du bas de la vue, mais ne tiendra compte de son emplacement que lors d'un déplacement vers la droite (il ignore les mouvements verticaux). Ainsi, même si le bas monte et descend, il s'animera correctement avec OnSwipe.
- Mettez à jour
OnSwipepour suivre correctement le mouvement de la lune.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
Essayer
- Exécutez à nouveau l'application et essayez de faire glisser la lune sur toute la trajectoire. Même si elle suit un arc complexe,
MotionLayoutpourra faire progresser l'animation en réponse aux événements de balayage.

11. Exécuter le mouvement avec du code
MotionLayout peut être utilisé pour créer des animations riches lorsqu'il est associé à CoordinatorLayout. Au cours de cette étape, vous allez créer un en-tête réductible à l'aide de MotionLayout.
Étape 1 : Explorez le code existant
- Pour commencer, ouvrez
layout/activity_step8.xml. - Dans
layout/activity_step8.xml, vous pouvez voir qu'unCoordinatorLayoutet unAppBarLayoutfonctionnels sont déjà intégrés.
activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Cette mise en page utilise un CoordinatorLayout pour partager les informations de défilement entre NestedScrollView et AppBarLayout. Ainsi, lorsque NestedScrollView défile vers le haut, il informe AppBarLayout du changement. C'est ainsi que vous implémentez une barre d'outils réductible comme celle-ci sur Android : le défilement du texte sera "coordonné" avec l'en-tête réductible.
La scène de mouvement vers laquelle @id/motion_layout pointe est semblable à celle de la dernière étape. Toutefois, la déclaration OnSwipe a été supprimée pour permettre à CoordinatorLayout de fonctionner.
- Exécutez l'application et accédez à l'étape 8. Vous remarquerez que lorsque vous faites défiler le texte, la lune ne bouge pas.
Étape 2 : Faire défiler le MotionLayout
- Pour que la vue
MotionLayoutdéfile dès queNestedScrollViewdéfile, ajoutezmotion:minHeightetmotion:layout_scrollFlagsàMotionLayout.
activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- Exécutez à nouveau l'application et passez à l'étape 8. Vous pouvez voir que
MotionLayoutse réduit lorsque vous faites défiler la page vers le haut. Toutefois, l'animation ne progresse pas encore en fonction du comportement de défilement.
Étape 3 : Déplacez le mouvement avec du code
- Ouvrez
Step8Activity.kt. Modifiez la fonctioncoordinateMotion()pour informerMotionLayoutdes modifications de la position de défilement.
Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
Ce code enregistre un OnOffsetChangedListener qui sera appelé chaque fois que l'utilisateur fera défiler l'écran avec le décalage de défilement actuel.
MotionLayout permet de rechercher sa transition en définissant la propriété de progression. Pour convertir une verticalOffset en pourcentage de progression, divisez-la par la plage de défilement totale.
Essayer
- Déployez à nouveau l'application et exécutez l'animation de l'étape 8. Vous voyez que
MotionLayoutfait progresser l'animation en fonction de la position de défilement.

Il est possible de créer des animations personnalisées de barre d'outils réductible dynamique à l'aide de MotionLayout. En utilisant une séquence de KeyFrames, vous pouvez obtenir des effets très gras.
12. Félicitations
Cet atelier de programmation a abordé l'API de base de MotionLayout.
Pour voir d'autres exemples de MotionLayout en pratique, consultez l'exemple officiel. N'oubliez pas de consulter la documentation.
En savoir plus
MotionLayout est compatible avec encore plus de fonctionnalités qui ne sont pas abordées dans cet atelier de programmation, comme KeyCycle, qui vous permet de contrôler les chemins ou les attributs avec des cycles répétitifs, et KeyTimeCycle, qui vous permet d'animer en fonction de l'heure. Consultez les exemples pour voir comment les utiliser.
Pour accéder aux autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Android avancé en Kotlin.