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 si vous suivez 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 avancés pour Android en Kotlin.
MotionLayout
est une bibliothèque qui vous permet d'ajouter des animations enrichies à votre application Android. Il est basé sur ConstraintLayout,
et vous permet d'animer tout ce que vous pouvez compiler à l'aide de ConstraintLayout
.
Vous pouvez utiliser MotionLayout
pour animer simultanément l'emplacement, la taille, la visibilité, la valeur alpha, la couleur, l'élévation, la rotation et d'autres attributs de plusieurs vues. À l'aide du code XML déclaratif, vous pouvez créer des animations coordonnées impliquant plusieurs vues, qui sont difficiles à réaliser avec du code.
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 permet à l'utilisateur de suivre naturellement les modifications apportées à votre interface utilisateur.
- Attirez l'attention : utilisez des animations pour attirer l'attention sur des éléments importants de l'interface utilisateur.
- Créez de magnifiques graphismes : des mouvements de conception efficaces rendent les applications plus attrayantes.
Prérequis
Cet atelier de programmation s'adresse aux développeurs ayant une certaine expérience du développement Android. Avant de suivre cet atelier de programmation, vous devez:
- Apprenez à 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 la mise en page avec contrainte.
Objectifs de l'atelier
- Définir une animation avec
ConstraintSets
etMotionLayout
- Animation basée sur des événements de déplacement
- Modifier l'animation avec
KeyPosition
- Modifier les attributs avec
KeyAttribute
- Exécuter des animations avec du code
- Animer des en-têtes réductibles avec
MotionLayout
Prérequis
- Android Studio 4.0 (l'éditeur
MotionLayout
ne 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
Tout d'abord, vous allez créer une animation qui déplace une vue du début vers le bas de l'écran en réponse à un clic de l'utilisateur.
Pour créer une animation à partir du code de démarrage, vous avez 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 qui décrit une animation pourMotionLayout.
Transition,
qui fait partie deMotionScene
qui spécifie la durée de l'animation, le déclencheur et comment déplacer les vues.- Élément
ConstraintSet
qui spécifie à la fois les contraintes start et end de la transition.
Examinons chacun de ces éléments l'un après l'autre, en commençant par MotionLayout
.
Étape 1: Explorez le code existant
MotionLayout
est une sous-classe de ConstraintLayout
. Il prend donc en charge les mêmes fonctionnalités lors de l'ajout d'une animation. Pour utiliser MotionLayout
, vous ajoutez une vue MotionLayout
où vous utiliseriez ConstraintLayout.
.
- Dans
res/layout
, ouvrezactivity_step1.xml.
. Vous avez ici unConstraintLayout
contenant un seulImageView
d'étoile, avec une teinte appliquée.
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écutiez l'application maintenant, vous verrez l'affichage des étoiles sans contrainte, ce qui signifie qu'ils seraient positionnés dans un emplacement inconnu. Android Studio affiche un avertissement concernant l'absence de contraintes.
Étape 2: Passer à la mise en page de mouvement
Pour créer une animation à l'aide de MotionLayout,
, vous devez convertir ConstraintLayout
en MotionLayout
.
Pour que votre mise en page utilise une scène de mouvement, elle doit pointer vers elle.
- Pour ce faire, ouvrez la surface de conception. Dans Android Studio 4.0, vous ouvrez la surface de conception à l'aide de l'icône de fractionnement ou de conception en haut à droite lorsque vous consultez un fichier XML de mise en page.
- Une fois que vous avez ouvert la surface de conception, effectuez un clic droit sur l'aperçu, puis 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 votre élément en MotionLayout
, la surface de conception affiche l'éditeur de mouvement
L'Éditeur de mouvement comprend trois nouveaux éléments d'interface utilisateur:
- Overview (Aperçu) : cette sélection modale vous permet de sélectionner différentes parties de l'animation. Dans cette image, la
ConstraintSet
start
est sélectionnée. Vous pouvez également sélectionner la transition entrestart
etend
en cliquant sur la flèche entre les éléments. - Section : sous l'aperçu, une fenêtre de section change en fonction de l'élément actuellement sélectionné. Dans cette image, les informations
ConstraintSet
start
s'affichent dans la fenêtre de sélection. - Attribut : le panneau des attributs s'affiche et vous permet de modifier les attributs de l'élément sélectionné depuis la fenêtre de présentation ou de sélection. Dans cette image, il s'agit des attributs du
ConstraintSet
start
.
É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 ce à quoi ressemble l'écran avant l'animation et la fin décrit ce à quoi ressemble l'écran une fois l'animation terminée. MotionLayout
est responsable de l'animation 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. Comme son nom l'indique, une ConstraintSet
est un ensemble de contraintes qui peuvent être appliquées aux vues. Cela inclut les contraintes de largeur, de hauteur et de ConstraintLayout
. Il inclut également certains attributs tels que alpha
. Il ne contient pas les vues elles-mêmes, mais uniquement les contraintes qui s'appliquent à ces vues.
Toutes les contraintes spécifiées dans un ConstraintSet
remplacent 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 celles de MotionScene
sont appliquées.
Au cours de cette étape, vous allez faire en sorte que l'affichage des étoiles commence en haut au début de l'écran et se termine en bas de l'écran.
Vous pouvez effectuer cette étape dans l'Éditeur de mouvement ou en modifiant directement le texte de activity_step1_scene.xml
.
- Sélectionnez le ConstraintSet
start
dans le panneau de présentation
- Dans le panneau de sélection, sélectionnez
red_star
. La source s'affiche actuellement pourlayout
, ce qui signifie qu'il n'est pas limité dans cetteConstraintSet
. Utilisez l'icône en forme de crayon en haut à droite pour créer une contrainte.
- Vérifiez que
red_star
affiche une source destart
lorsquestart
ConstraintSet
est sélectionné dans le panneau d'aperçu. - Dans le panneau "Attributes" (Attributs), avec
red_star
sélectionné dansstart
ConstraintSet
, ajoutez une contrainte en haut et commencez par cliquer sur les boutons + bleus.
- Ouvrez
xml/activity_step1_scene.xml
pour afficher le code généré par l'Éditeur de mouvement 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>
Le id
de ConstraintSet
est @id/start
et spécifie toutes les contraintes à appliquer à toutes les vues de MotionLayout
. Comme ce MotionLayout
ne comporte qu'une seule vue, il n'a besoin que d'un Constraint
.
Le Constraint
dans 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 des contraintes et des 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 requises pour contraindre la vue red_star
au début de son parent.
- Sélectionnez le ConstraintSet
end
dans le panneau de présentation.
- Suivez la même procédure que précédemment pour ajouter un
Constraint
pourred_star
dans leend
ConstraintSet
. - Pour utiliser l'Éditeur de mouvement afin d'effectuer cette étape, ajoutez une contrainte à
bottom
etend
en 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>
Tout comme @id/start
, ce ConstraintSet
possède un seul Constraint
sur @id/red_star
. Cette fois, l'application applique une contrainte sur l'extrémité inférieure de l'écran.
Il n'est pas nécessaire de les nommer @id/start
et @id/end
, mais cette opération 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 des ConstraintSet
de début et de fin. Une transition peut également spécifier d'autres manières de modifier l'animation, par exemple sa durée d'exécution ou la manière de l'animer en faisant glisser des vues.
- L'Éditeur de mouvement crée une transition par défaut pour nous lors de la création du fichier MotionScene. Ouvrez
activity_step1_scene.xml
pour voir 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. Examiner chaque attribut:
constraintSetStart
sera appliqué aux vues au début de l'animation.constraintSetEnd
sera appliqué aux vues à la fin de l'animation.duration
spécifie la durée de l'animation en millisecondes.
MotionLayout
recherche alors un chemin entre les contraintes de début et de fin et l'anime pendant la durée spécifiée.
Étape 5: Prévisualiser l'animation dans l'Éditeur de mouvement
Animation:vidéo de lecture d'un aperçu de transition dans l'éditeur de mouvement
- Ouvrez l'Éditeur de mouvement, puis sélectionnez la transition en cliquant sur la flèche entre
start
etend
dans le panneau de présentation.
- Le panneau selection (sélection) affiche les commandes de lecture et une barre de lecture 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 devez trouver un moyen de lancer l'animation. Pour ce faire, vous pouvez faire en sorte que MotionLayout
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 de présentation.
- Cliquez sur Créer un gestionnaire de clics ou de balayages dans la barre d'outils du panneau de présentation . Cette action ajoute un gestionnaire qui lancera une transition.
- Sélectionnez Gestionnaire de clics dans la fenêtre pop-up.
- Définissez View To Click (Afficher pour cliquer) sur
red_star
.
- Cliquez sur Ajouter. Dans l'Éditeur de mouvement, le gestionnaire de clics est représenté par un petit point au niveau de la transition.
- Une fois la transition sélectionnée dans le panneau "Overview" (Aperçu), ajoutez un attribut
clickAction
detoggle
au gestionnaire OnClick que vous venez d'ajouter dans le panneau des attributs.
- Ouvrez
activity_step1_scene.xml
pour voir le code généré par l'Éditeur de mouvement
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>
. Examiner chaque attribut:
targetId
est la vue à surveiller pour les clics.clickAction
surtoggle
passera de l'état de début à l'état de fin lors d'un clic. Vous pouvez découvrir d'autres options pourclickAction
dans la documentation.
- Exécutez votre code, cliquez sur l'étape 1, puis sur l'étoile rouge et observez l'animation.
Étape 5: Les animations en action
Exécutez l'application ! Votre animation doit s'exécuter lorsque vous cliquez sur l'étoile.
Le fichier de scène de mouvement finalisé définit un élément Transition
qui pointe vers une ConstraintSet
de début et de fin.
Au début de l'animation (@id/start
), l'icône en forme d'étoile se limite au début supérieur de l'écran. À la fin de l'animation (@id/end
), l'icône en forme d'étoile se trouve au bas 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. Animation basée sur des événements de déplacement
Au cours de cette étape, vous allez créer une animation qui répond à un événement de déplacement de l'utilisateur (lorsque l'utilisateur balaie l'écran) pour exécuter l'animation. MotionLayout
prend en charge le suivi des événements tactiles pour déplacer les vues, ainsi que des gestes de glissement d'un geste vif basés sur les lois de la physique pour fluidifier le mouvement.
Étape 1: Inspectez le code initial
- Pour commencer, ouvrez le fichier de mise en page
activity_step2.xml
, qui contient déjà unMotionLayout
. Examinez 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 icônes à trois étoiles ne sont pas limitées dans la mise en page, car elles sont animées dans la scène de mouvement.
Des contraintes sont appliquées au crédit TextView
, car il reste au même endroit pendant toute l'animation et ne modifie aucun attribut.
Étape 2: Animez la scène
Comme la dernière animation, elle est définie par une ConstraintSet,
de début et de fin, ainsi qu'une Transition
.
Définit le ConstraintSet de départ.
- Ouvrez la scène de mouvement
xml/step2.xml
pour 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 valeuralpha
de0.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 cette ConstraintSet
, vous spécifiez une valeur Constraint
pour chacune des étoiles. Chaque contrainte sera appliquée par MotionLayout
au début de l'animation.
Chaque vue en étoile est centrée en bas de l'écran en fonction des contraintes de début, de fin et de bas. Les deux étoiles @id/left_star
et @id/right_star
ont toutes deux 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 appliquée au début (motion:layout_constraintStart_toStartOf
, par exemple) contraint le début d'une vue au début d'une autre. Cela peut prêter à confusion au début, car le nom start
est utilisé à la fois et dans le contexte de contraintes. Pour faire la distinction, le start
dans layout_constraintStart
fait référence au "début" de la vue, qui est celle de gauche
dans une langue de gauche à droite et celle de droite dans une langue de droite à gauche. L'ensemble de contraintes start
fait référence au début de l'animation.
Définir le ConstraintSet final
- Définissez la contrainte de fin afin d'utiliser une chaîne pour positionner les trois étoiles sous
@id/credits
. De plus, la valeur de fin de l'alpha
des étoiles gauche et droite sera définie 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>
Au final, les vues s'étendent et s'étendent à partir du centre au fur et à mesure de leur animation.
De plus, comme la propriété alpha
est définie sur @id/right_start
et @id/left_star
dans les deux vues ConstraintSets
, les deux vues s'affichent en fondu à mesure que l'animation progresse.
Animation basée sur le balayage de l'utilisateur
MotionLayout
peut suivre les événements de déplacement de l'utilisateur, ou un balayage, pour créer un "glissement d'un geste vif" basé sur les lois de la physique de l'animation. Cela signifie que les vues se poursuivront si l'utilisateur les déplace et ralentiront comme le ferait un objet physique lorsqu'il roulerait sur une surface. Vous pouvez ajouter ce type d'animation avec une balise OnSwipe
dans Transition
.
- Remplacez TODO pour ajouter une balise
OnSwipe
par<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, le plus important étant touchAnchorId
.
touchAnchorId
est la vue suivie qui se déplace en réponse à un appui.MotionLayout
maintient cette vue à la même distance du doigt qui balaie l'écran.touchAnchorSide
détermine quel côté de la vue doit être suivi. Ceci 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.dragDirection
détermine la direction de l'animation (vers le haut, le bas, la gauche ou la droite).
Lorsque MotionLayout
écoute des événements de déplacement, l'écouteur est enregistré dans la vue MotionLayout
, et non dans la vue 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, si l'utilisateur touche 100 dp du côté de l'ancre, MotionLayout
l'éloigne de 100 dp de son doigt pendant toute l'animation.
Essayer
- Exécutez à nouveau l'application et ouvrez l'écran de l'étape 2. L'animation s'affiche.
- Essayez de faire glisser d'un geste vif Vous pouvez aussi relâcher le doigt au milieu de l'animation pour découvrir comment
MotionLayout
affiche des animations basées sur la physique des fluides.
MotionLayout
peut animer des conceptions très différentes à 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 pour commencer. À 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ée une animation fluide entre le début et la fin.
5. Modifier un chemin d'accès
Au cours de cette étape, vous allez créer une animation qui suit un tracé complexe pendant l'animation et anime les crédits pendant le mouvement. MotionLayout
peut modifier le chemin qu'une vue prendra entre le début et la fin à l'aide d'un KeyPosition
.
Étape 1: Explorez le code existant
- Ouvrez
layout/activity_step3.xml
etxml/step3.xml
pour afficher la mise en page et la scène d'animation existantes.ImageView
etTextView
affichent la lune et le texte des crédits. - Ouvrez le fichier de scène de mouvement (
xml/step3.xml
). Vous constatez qu'un élémentTransition
de@id/start
à@id/end
est défini. L'animation déplace l'image de lune du coin inférieur gauche de l'écran vers le bas à droite de l'écran à l'aide de deuxConstraintSets
. Le texte des crédits apparaît en fondu 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 un trajet linéaire (ou une ligne droite) du début à la fin lorsque vous cliquez dessus.
Étape 2: Activer le débogage du chemin d'accès
Avant d'ajouter un arc au mouvement de la Lune, il est utile d'activer le débogage du trajet dans MotionLayout
.
Pour développer des animations complexes avec MotionLayout
, vous pouvez dessiner le chemin d'animation de chaque vue. Cette fonctionnalité est utile lorsque vous souhaitez visualiser votre animation et affiner les moindres détails du mouvement.
- Pour activer les chemins de débogage, ouvrez
layout/activity_step3.xml
et ajoutezmotion:motionDebug="SHOW_PATH"
à la baliseMotionLayout
.
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
Après avoir activé le débogage du chemin d'accès, lorsque vous réexécuterez l'application, les chemins de toutes les vues s'afficheront à l'aide d'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 trajet d'une vue.
- Les losanges représentent un
KeyPosition
qui modifie le tracé.
Par exemple, dans cette animation, le cercle central correspond à la position du texte des crédits.
Étape 3: Modifiez le chemin d'accès
Toutes les animations de MotionLayout
sont définies par un élément ConstraintSet
de début et de fin qui définit l'apparence de l'écran avant le début et après la fin de l'animation. Par défaut, MotionLayout
trace un tracé linéaire (une ligne droite) entre les positions de début et de fin de chaque vue qui change de position.
Pour construire des tracés complexes comme l'arc de la lune dans cet exemple, MotionLayout
utilise un KeyPosition
afin de modifier le trajet emprunté par une vue entre le début et la fin.
- Ouvrez
xml/step3.xml
et ajoutez unKeyPosition
à la scène. La baliseKeyPosition
est 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.
Étant donné que MotionLayout
calcule le trajet de la Lune entre le début et la fin, il modifie le tracé en fonction de la valeur KeyPosition
spécifiée dans le KeyFrameSet
. Vous pouvez voir comment cela modifie le chemin d'accès en exécutant à nouveau l'application.
Un élément KeyPosition
comporte plusieurs attributs qui décrivent la manière dont il modifie le chemin d'accès. Voici les plus importants:
framePosition
est un nombre compris entre 0 et 100. Il définit à quel moment, dans l'animation, ceKeyPosition
doit être appliqué, 1 correspondant à 1% pendant l'animation et 99 à 99% pendant l'animation. Ainsi, si la valeur est de 50, vous l'appliquez au milieu.motionTarget
est la vue pour laquelle cetteKeyPosition
modifie le chemin.keyPositionType
est la façon dont cetteKeyPosition
modifie le chemin d'accès. Il peut s'agir deparentRelative
,pathRelative
oudeltaRelative
(comme expliqué à l'étape suivante).percentX | percentY
correspond au degré de modification du chemin àframePosition
(valeurs comprises entre 0,0 et 1,0, avec des valeurs négatives et des valeurs supérieures à 1 autorisées).
Voici comment procéder : "À framePosition
, modifiez la trajectoire de motionTarget
en la déplaçant de percentX
ou de percentY
selon les coordonnées déterminées par keyPositionType
.
Par défaut, MotionLayout
arrondit tous les angles introduits en modifiant le tracé. Si vous regardez l'animation que vous venez de créer, vous pouvez voir que la lune suit une trajectoire incurvée au niveau du virage. C'est ce que vous souhaitez pour la plupart des animations. Si ce n'est pas le cas, vous pouvez spécifier l'attribut curveFit
pour la personnaliser.
Essayer
Si vous exécutez à nouveau l'application, l'animation de cette étape s'affiche.
La Lune suit un arc car elle traverse l'KeyPosition
spécifié dans l'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 tracé de motionTarget
@id/moon
en le déplaçant de 50% Y
(à mi-chemin vers le bas de l'écran) en fonction des coordonnées déterminées par parentRelative
(l'ensemble du MotionLayout
)."
Ainsi, à la moitié de l'animation, la lune doit traverser un KeyPosition
situé à 50% de l'écran. Ce KeyPosition
ne modifie pas du tout le mouvement X. La lune continuera donc d'aller horizontalement du début à la fin. MotionLayout
trouvera un trajet fluide qui traverse cette KeyPosition
en se déplaçant entre le début et la fin.
Si vous regardez attentivement, le texte des crédits est limité par la position de la lune. Pourquoi ne se déplace-t-elle pas 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 le trajet que suit la Lune, ses positions de début et de fin ne la déplacent pas verticalement. KeyPosition
ne modifie pas la position de début ni de fin. Le texte des crédits est donc limité à la position finale de la lune.
Si vous voulez 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 découvrirez les différents types de keyPositionType
dans MotionLayout
.
6. Comprendre le keyPositionType
Lors de la dernière étape, vous avez utilisé un keyPosition
de type parentRelative
pour décaler le chemin de 50% de l'écran. L'attribut keyPositionType
détermine la façon dont MotionLayout modifiera le tracé en fonction de percentX
ou de percentY
.
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Il existe trois types de keyPosition
différents: parentRelative
, pathRelative
et deltaRelative
. La spécification d'un type modifie le système de coordonnées par lequel les valeurs percentX
et percentY
sont calculées.
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. Ils sont également utiles pour décrire une position à l'écran.
Les systèmes de coordonnées MotionLayout
sont un système de coordonnées cartésien. Cela signifie qu'elles ont un axe X et un axe Y défini par deux lignes perpendiculaires. La principale différence 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. Ils autorisent les valeurs négatives et les valeurs supérieures à 1.0
. Par exemple, une valeur percentX
de -2.0
signifierait d'aller deux fois dans le sens opposé de l'axe X.
Si cela ressemble un peu trop au cours d'algèbre, regardez les images ci-dessous !
parentRelative coordonnées
L'élément keyPositionType
de parentRelative
utilise le même système de coordonnées que l'écran. Elle définit (0, 0)
en haut à gauche de l'ensemble de la 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'intégralité de la MotionLayout
, comme l'arc lunaire dans cet exemple.
Toutefois, si vous souhaitez modifier un tracé par rapport au mouvement, par exemple pour la rendre légèrement incurvée, les deux autres systèmes de coordonnées sont plus adaptés.
Coordonnées deltaRelative
Delta est un terme mathématique qui désigne le changement. deltaRelative
est donc une façon de dire "changer la relation relative". Dans deltaRelative
coordonnées, (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 avec l'écran.
L'axe X est toujours horizontal à 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 est déplacée.
deltaRelative
est un système de coordonnées idéal pour contrôler les mouvements horizontal ou vertical de manière isolée. Par exemple, vous pouvez créer une animation qui termine uniquement son mouvement vertical (Y) à 50 % et continue à s'animer horizontalement (X).
Coordonnées pathRelative
Le dernier système de coordonnées de 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)
correspond donc à la position de départ et (1,0)
à la position de fin.
Pourquoi le choisir ? C'est assez surprenant à première vue, d'autant plus que ce système de coordonnées n'est même pas aligné sur celui de l'écran.
Il s'avère que pathRelative
est très utile dans plusieurs cas.
- Accélérez, ralentissez ou arrêtez une vue pendant une partie de l'animation. Étant donné que la dimension X correspondra toujours exactement au chemin emprunté par la vue, vous pouvez utiliser un
KeyPosition
pathRelative
pour modifier laframePosition
atteinte à un point particulier de ce chemin. Ainsi, unKeyPosition
àframePosition="50"
avec unepercentX="0.1"
obligerait l'animation à mettre 50% du temps à parcourir les 10 premiers% du mouvement. - Ajouter un arc subtil à un tracé Étant donné que la dimension Y est toujours perpendiculaire au mouvement, la modification de Y modifie la trajectoire en courbe par rapport au mouvement global.
- Ajouter une deuxième dimension lorsque
deltaRelative
ne fonctionne pas. Pour un mouvement entièrement horizontal et vertical,deltaRelative
ne crée qu'une seule dimension utile. Cependant,pathRelative
crée toujours des coordonnées X et Y utilisables.
À l'étape suivante, vous allez apprendre à créer des chemins d'accès encore plus complexes en utilisant plusieurs KeyPosition
.
7. Construire des chemins complexes
L'animation créée à la dernière étape crée une courbe lisse, mais la forme pourrait ressembler davantage à la lune.
Modifier un tracé avec plusieurs éléments KeyPosition
MotionLayout
peut modifier davantage un tracé en définissant autant de KeyPosition
que nécessaire pour générer un mouvement. Pour cette animation, vous allez construire un arc, mais vous pouvez faire sauter la lune de haut en bas au milieu de l'écran, si vous le souhaitez.
- Ouvrez
xml/step4.xml
. Vous voyez qu'il possède les mêmes vues et leKeyFrame
que vous avez ajouté à la dernière étape. - Pour arrondir le haut de la courbe, ajoutez deux autres
KeyPositions
au tracé de@id/moon
: un juste avant qu'elle atteigne le sommet et une 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% du chemin d'accès de l'animation, et entraîneront le déplacement de @id/moon
sur un tracé situé à 60% du haut de l'écran. Combiné à l'KeyPosition
existant à 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 d'animation souhaitée. MotionLayout
appliquera chaque KeyPosition
à la valeur framePosition
spécifiée et déterminera comment créer un mouvement lisse qui traverse toutes les KeyPositions
.
Essayer
- Exécutez à nouveau l'application. Passez à l'étape 4 pour voir l'animation en action. Lorsque vous cliquez sur la Lune, elle suit son tracé du début à la fin, en passant par chaque
KeyPosition
spé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 simplement KeyPosition
.
Voici un exemple qui montre comment créer un tracé complexe qui se déplace dans les deux sens 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 fini d'explorer KeyPosition
, vous allez passer à d'autres types de KeyFrames
à l'étape suivante.
8. Modification d'attributs pendant le mouvement
Pour créer des animations dynamiques, vous devez souvent modifier la size
, la rotation
ou la 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
.
Au cours de cette étape, vous allez utiliser KeyAttribute
pour définir l'échelle de lune et la faire pivoter. Vous allez également utiliser un KeyAttribute
pour retarder l'affichage du texte jusqu'à ce que la Lune ait presque terminé son trajet.
Étape 1: Redimensionnez et faites pivoter avec KeyAttribute
- Ouvrez
xml/step5.xml
, qui contient la même animation que celle créée à la dernière étape. Pour plus de variété, cet écran utilise une image de l'espace différente comme arrière-plan. - Pour agrandir et faire pivoter la lune, ajoutez deux balises
KeyAttribute
dansKeyFrameSet
à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ées à 50% et 100% de l'animation. La première KeyAttribute
à 50% se produira en haut de l'arc, et entraînera le doublement de la taille de la vue et une rotation de -360 degrés (ou un cercle complet). Le deuxième KeyAttribute
termine la deuxième rotation à -720 degrés (deux cercles complets) et réduit la taille à la taille normale, car les valeurs scaleX
et scaleY
sont définies par défaut sur 1,0.
Tout comme une KeyPosition
, un KeyAttribute
utilise framePosition
et motionTarget
pour spécifier quand appliquer le KeyFrame
et quelle vue modifier. MotionLayout
effectue une interpolation entre KeyPositions
pour créer des animations fluides.
KeyAttributes
acceptent les attributs qui peuvent être appliqués à toutes les vues. Ils sont compatibles avec la modification des attributs de base tels que visibility
, alpha
ou elevation
. Vous pouvez également modifier la rotation comme vous le faites ici, effectuer une rotation en trois dimensions avec rotateX
et rotateY
, redimensionner 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 consiste à mettre à jour l'animation afin que le texte des crédits n'apparaisse pas tant que l'animation n'est pas entièrement terminée.
- Pour retarder l'affichage des crédits, définissez une autre
KeyAttribute
qui garantit quealpha
restera sur 0 jusqu'àkeyPosition="85"
.MotionLayout
effectuera toujours une transition fluide de la version alpha de 0 à 100, mais sur les 15% restants 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
conserve la alpha
de @id/credits
à 0,0 pour les premiers 85% de l'animation. Puisqu'elle commence à une valeur alpha de 0, elle sera invisible pendant les premiers 85% de l'animation.
L'effet final de cette 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 s'installe dans le coin droit de l'écran.
En décalant les animations d'une vue pendant qu'une autre se déplace ainsi, vous pouvez créer des animations impressionnantes et 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 son trajet du début à la fin, en passant par chaque
KeyAttribute
spécifié dans lesKeyFrameSet
.
Comme vous faites pivoter la Lune de deux cercles complets, elle effectuera désormais un double retournement, et les crédits retarderont leur affichage jusqu'à ce que l'animation soit presque terminée.
Explorer par vous-même
Avant de passer au type final de KeyFrame
, essayez de modifier d'autres attributs standards dans KeyAttributes
. Par exemple, essayez de remplacer rotation
par rotationX
pour voir l'animation générée.
Voici une liste des attributs standards que vous pouvez essayer:
android:visibility
android:alpha
android:elevation
android:rotation
android:rotationX
android:rotationY
android:scaleX
android:scaleY
android:translationX
android:translationY
android:translationZ
9. Modifier des attributs personnalisés
Les animations enrichies impliquent la modification de la couleur ou d'autres attributs d'une vue. Bien que MotionLayout
puisse utiliser un KeyAttribute
pour modifier n'importe quel attribut standard listé 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 associée à un setter. Par exemple, vous pouvez définir le paramètre 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 un 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 créée à l'étape précédente. - Pour que la lune change de couleur, ajoutez deux
KeyAttribute
avec unCustomAttribute
dans 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:attributeName
est le nom du setter qui sera appelé par cet attribut personnalisé. Dans cet exemple,setColorFilter
surDrawable
sera appelé.motion:custom*Value
est une valeur personnalisée dont le type est indiqué dans le nom. Dans cet exemple, la valeur personnalisée est une couleur spécifiée.
Les valeurs personnalisées peuvent avoir l'un des types suivants:
- Couleur
- Entier
- Float
- Chaîne
- Dimension
- Booléen
À l'aide de cette API, MotionLayout
peut animer tout ce qui fournit un setter sur n'importe quelle vue.
Essayer
- Exécutez à nouveau l'application et passez à l'étape 6 pour voir l'animation en action. Lorsque vous cliquez sur la lune, elle suit son trajet du début à la fin, en passant par chaque
KeyAttribute
spécifié dans lesKeyFrameSet
.
Lorsque vous ajoutez d'autres KeyFrames
, MotionLayout
modifie la trajectoire de la Lune en la faisant passer d'une ligne droite à une courbe complexe, ce qui ajoute un double saut périlleux arrière, un redimensionnement et un changement de couleur au milieu de l'animation.
Dans les animations réelles, vous animez souvent plusieurs vues en même temps en contrôlant leur mouvement le long de différents chemins et à différentes vitesses. En spécifiant un KeyFrame
différent pour chaque vue, il est possible de créer une chorégraphie d'animations riches qui animent plusieurs vues avec MotionLayout
.
10. Événements de déplacement et chemins complexes
Au cours de cette étape, vous allez apprendre à utiliser OnSwipe
avec des chemins d'accès complexes. Jusqu'à présent, l'animation de la Lune a été déclenchée par un écouteur OnClick
et dure une durée fixe.
Pour contrôler des animations dont les tracés sont complexes à l'aide de OnSwipe
, comme l'animation lunaire que vous avez créée au cours des dernières étapes, vous devez comprendre le fonctionnement de OnSwipe
.
Étape 1: Explorez le comportement d'OnSwipe
- Ouvrez
xml/step7.xml
et recherchez la déclarationOnSwipe
existante.
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. Essayez de produire une animation fluide en faisant glisser la lune le long de la trajectoire de l'arc.
Cette animation n'est pas très satisfaisante lorsque vous l'exécutez. Lorsque la Lune atteint le sommet de l'arc, elle commence à sauter.
Pour comprendre le bug, réfléchissez à ce qui se passe lorsque l'utilisateur appuie juste en dessous de la partie supérieure de l'arc. Comme la balise OnSwipe
comporte un élément motion:touchAnchorSide="bottom"
, MotionLayout
tentera de maintenir la distance entre le doigt et le bas de la vue constant tout au long de l'animation.
Cependant, comme le bas de la Lune ne va pas toujours dans la même direction, il monte, puis revient vers le bas. MotionLayout
ne sait donc pas quoi faire lorsque l'utilisateur vient de passer le haut de l'arc. Sachant cela, puisque vous suivez le bas de la Lune, où doit-il être placé lorsque l'utilisateur touche ici ?
Étape 2: Utilisez le côté droit
Pour éviter ce type de bug, il est important de toujours choisir une touchAnchorId
et une touchAnchorSide
qui progressent toujours dans une direction pendant toute la durée de l'animation.
Dans cette animation, les côtés right
et left
de la Lune progresseront tous deux à l'écran dans une direction.
Cependant, bottom
et top
vont s'inverser. Lorsque OnSwipe
tente de le suivre, il est confus quand sa direction change.
- Pour que cette animation suive les événements tactiles, définissez
touchAnchorSide
surright
.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Étape 3: Utilisez dragDirection
Vous pouvez également combiner dragDirection
et touchAnchorSide
pour modifier la direction d'un rail latéral. Il est toujours important que touchAnchorSide
progresse dans une seule direction, mais vous pouvez indiquer à MotionLayout
dans quelle direction effectuer le suivi. Par exemple, vous pouvez conserver touchAnchorSide="bottom"
, mais ajouter dragDirection="dragRight"
. MotionLayout
suivra la position du bas de la vue, mais ne tiendra compte de sa position que lorsqu'il se déplacera vers la droite (les mouvements verticaux seront ignorés). Ainsi, même si la partie inférieure monte et descend, l'animation s'effectue toujours correctement avec OnSwipe
.
- Mettez à jour
OnSwipe
pour 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 le long du tracé. Même s'il suit un arc complexe,
MotionLayout
peut faire progresser l'animation en réponse aux événements de balayage.
11. Mouvement en cours avec du code
MotionLayout
permet de créer des animations enrichies avec 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 constatez qu'unCoordinatorLayout
et unAppBarLayout
fonctionnels sont déjà créé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 des 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 qui peut être réduite comme celle-ci sur Android : le défilement du texte sera "coordonné". avec l'en-tête escamotable.
La scène de mouvement vers laquelle pointe @id/motion_layout
est semblable à la scène de mouvement de la dernière étape. Cependant, la déclaration OnSwipe
a été supprimée pour lui permettre de fonctionner avec CoordinatorLayout
.
- Exécutez l'application et passez à l'étape 8. Vous voyez que lorsque vous faites défiler le texte, la lune ne bouge pas.
Étape 2: Faire défiler MotionLayout
- Pour faire défiler la vue
MotionLayout
dès queNestedScrollView
défile, ajoutezmotion:minHeight
etmotion: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 constatez que
MotionLayout
se 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 indiquer àMotionLayout
les changements de 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 fait défiler la page 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 valeur 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.
MotionLayout
fait progresser l'animation en fonction de la position de défilement.
Il est possible de créer des animations personnalisées pour la barre d'outils de réduction dynamique à l'aide de MotionLayout
. En utilisant une séquence de KeyFrames
, vous pouvez obtenir des effets très audacieux.
12. Félicitations
Cet atelier de programmation a couvert l'API de base de MotionLayout
.
Pour voir d'autres exemples pratiques de MotionLayout
, consultez l'exemple officiel. N'oubliez pas de consulter la documentation.
En savoir plus
MotionLayout
prend en charge 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étés, et KeyTimeCycle,
, qui vous permet d'animer des événements en fonction de l'heure. Consultez les exemples pour chaque méthode.
Pour obtenir des liens vers d'autres ateliers de ce cours, consultez la page de destination des ateliers de programmation avancés pour Android en Kotlin.