Animation simple avec Jetpack Compose

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

1. Avant de commencer

Dans cet atelier de programmation, vous allez apprendre à ajouter une animation simple à votre application Android. Les animations permettent de rendre votre application plus interactive, plus intéressante et plus facile à interpréter. Animer des changements spécifiques sur un écran rempli d'informations peut aider l'utilisateur à identifier ce qui change.

Vous pouvez utiliser de nombreux types d'animations dans l'interface utilisateur d'une application. Les éléments peuvent apparaître ou disparaître en fondu, se déplacer sur l'écran ou en dehors, ou être transformés de façon intéressante. Cela rend l'interface utilisateur de l'application plus lisible et plus facile à utiliser.

Les animations permettent également d'améliorer l'aspect général de l'application, pour la rendre plus élégante tout en simplifiant son utilisation.

Les animations qui récompensent l'utilisateur pour une tâche peuvent rendre les moments clés de son parcours plus significatifs.

Les éléments animés qui réagissent à l'utilisation du clavier permettent de confirmer que l'action est bien prise en compte.

Les éléments de liste animés réservent l'espace et indiquent que le contenu est en cours de chargement.

Animer une action de type "balayage pour ouvrir" encourage l'utilisateur à effectuer le geste requis.

Les animations peuvent clarifier ou compléter la signification des icônes.

Conditions préalables

  • Vous maîtrisez Kotlin, y compris les fonctions, et composables sans état.
  • Vous disposez de connaissances de base en création de mises en page dans Jetpack Compose.
  • Vous disposez de connaissances de base en création de listes dans Jetpack Compose.
  • Vous disposez de connaissances de base en Material Design.

Points abordés

  • Créer une animation de rétroaction simple avec Jetpack Compose

Objectifs de l'atelier

  • Vous allez utiliser l'application Woof (créée dans l'atelier Thématisation Material avec Jetpack Compose) pour ajouter une animation simple confirmant l'action de l'utilisateur.

Ce dont vous avez besoin

  • La dernière version d'Android Studio.
  • Une connexion Internet est nécessaire pour télécharger le code de démarrage.

2. Regarder la vidéo du code pas à pas (facultatif)

Si vous souhaitez voir un formateur réaliser cet atelier de programmation, regardez la vidéo ci-dessous.

Nous vous recommandons d'afficher la vidéo en plein écran (à l'aide de l'icône Ce symbole qui indique le mode plein écran représente quatre coins d'un carré mis en évidence. dans l'angle inférieur droit de la vidéo) pour mieux voir Android Studio et le code.

Cette étape est facultative. Vous pouvez également ignorer la vidéo et passer immédiatement aux instructions de l'atelier de programmation.

3. Présentation de l'application

Dans l'atelier de programmation Thématisation Material avec Jetpack Compose, vous avez créé à l'aide de Material Design une application Woof qui affiche une liste de chiens et les informations correspondantes.

7252aa244a54ad90.png

Dans cet atelier de programmation, vous allez ajouter une animation à cette application Woof. Vous ajouterez des informations sur votre passe-temps, qui s'afficheront lorsque l'élément de liste est développé. Vous ajouterez également une animation de rétroaction pour animer l'élément de liste en cours d'expansion :

1e9cf1dbc490924a.gif

Télécharger le code de démarrage

Pour commencer, téléchargez le code de démarrage :

(Télécharger le ZIP)

Vous pouvez également cloner le dépôt GitHub du code :

$ git clone
https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git
$ cd basic-android-kotlin-compose-training-woof
$ git checkout material

Vous pouvez parcourir le code dans le dépôt GitHub Woof app.

4. Ajouter une icône Développer

Avant d'implémenter une animation de rétroaction, vous devez ajouter une icône Développer f88173321938c003.png. L'icône Développer présente un bouton sur lequel l'utilisateur peut appuyer pour développer un élément de la liste.

9fbd3fb0daf35fd3.png

Icônes

Les icônes sont des symboles qui aident les utilisateurs à comprendre l'interface utilisateur en communiquant visuellement la fonction souhaitée. Elles s'inspirent souvent d'objets du monde physique que l'utilisateur est supposé connaître. La conception des icônes réduit souvent le niveau de détail au minimum requis pour être reconnues par l'utilisateur. Par exemple, dans le monde physique, un crayon sert à écrire. Une icône de crayon symbolise généralement la création ou la modification d'un élément.

Photo par Angelina Litvin, publiée sur Unsplash

Icône de crayon en noir et blanc

Material Design propose un certain nombre d'icônes classées dans des catégories courantes répondant à la plupart des besoins.

bfdb896506790c69.png

Ajouter une dépendance Gradle

Ajoutez la dépendance de bibliothèque material-icons-extended à votre projet. Vous utiliserez les icônes Icons.Filled.ExpandLess 30c384f00846e69b.png et Icons.Filled.ExpandMore f88173321938c003.png de cette bibliothèque.

  1. Dans le volet de projet, ouvrez Gradle Scripts > build.gradle (Module: Woof.app).

f7fe58e936bbad3e.png

  1. Faites défiler la page jusqu'à la fin du fichier build.gradle (Module: Woof.app). Dans le bloc dependencies{}, ajoutez la ligne suivante :
implementation "androidx.compose.material:material-icons-extended:$compose_version"

Ajouter l'icône composable

Ajoutez une fonction permettant d'afficher l'icône Développer à partir de la bibliothèque d'icônes Material et de l'utiliser comme bouton.

  1. Dans MainActivity.kt, après la fonction DogItem(), créez une fonction modulable nommée DogItemButton().
  2. Transmettez Boolean pour l'état développé, une expression lambda pour l'événement de clic sur le bouton et un Modifier facultatif, comme suit :
@Composable
private fun DogItemButton(
    expanded: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {

}
  1. Dans la fonction DogItemButton(), ajoutez un composable IconButton() qui accepte un paramètre nommé onClick et un lambda utilisant la syntaxe lambda finale, appelé lorsque l'utilisateur appuie sur cette icône et défini sur l'argument onClick transmis.
@Composable
private fun DogItemButton(
   // ...
) {
   IconButton(onClick = onClick) {

   }
}
  1. Dans le bloc lambda IconButton(), ajoutez un composable Icon avec un paramètre nommé appelé imageVector et définissez-le sur Icons.Filled.ExpandMore. Il s'agit de l'icône du bouton f88173321938c003.png, qui s'affiche à la fin de l'élément de liste. Android Studio affiche un avertissement pour les paramètres composables Icon(). Vous corrigerez ce problème ultérieurement.
  2. Ajoutez le paramètre nommé tint et définissez la couleur de l'icône sur MaterialTheme.colors.secondary. Ajoutez le paramètre nommé contentDescription et définissez-le sur la ressource de chaîne R.string.expand_button_content_description.
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore

IconButton(onClick = onClick) {
   Icon(
       imageVector = Icons.Filled.ExpandMore,
       tint = MaterialTheme.colors.secondary,
       contentDescription = stringResource(R.string.expand_button_content_description)
   )
}

Afficher l'icône

Affichez le composable DogItemButton() en l'ajoutant à la mise en page.

  1. Au début de la fonction modulable DogItem(), ajoutez un var pour enregistrer l'état développé de l'élément de liste. Définissez la valeur initiale sur false.
var expanded by remember { mutableStateOf(false) }
  1. À la fin du bloc Row de la fonction modulable DogItem(), appelez la fonction DogItemButton(), puis transmettez le lambda à l'état développé et le lambda vide pour le rappel. Ce code affiche l'icône du bouton dans l'élément de liste.
  2. Pour afficher l'icône du bouton dans l'élément de liste, dans la fonction modulable DogItem(), appelez DogItemButton() à la fin du bloc Row, après l'appel à DogInformation(). Transmettez l'état expanded et un lambda vide pour le rappel. Vous définirez cette fonction lambda ultérieurement.
Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
) {
   DogIcon(dog.imageResourceId)
   DogInformation(dog.name, dog.age)
   DogItemButton(
      expanded = expanded,
      onClick = { }
   )
}
  1. Appuyez sur Build & Refresh (Compiler et actualiser) pour actualiser l'aperçu dans le volet Design (Conception).

a49643f08701a8d.png

Notez que le bouton de développement n'est pas aligné à la fin des éléments de liste. Vous résoudrez ce problème à l'étape suivante.

Aligner les boutons "Développer"

Pour aligner les boutons de développement à la fin des éléments de liste, vous devez ajouter un espace vide dans la mise en page, avec l'attribut Modifier.weight().

Dans l'application Woof, chaque ligne de la liste contient une image et des informations sur le chien, ainsi qu'un bouton "Développer". Pour aligner correctement les icônes du bouton, vous devez ajouter un composable d'espace vide avant le bouton "Développer" et lui attribuer une pondération de 1f. Comme cet espace vide est le seul élément enfant pondéré de la ligne, il remplira l'espace restant après avoir mesuré la longueur de l'autre élément enfant non pondéré.

6c2b523849f0f626.png

Ajouter l'espace à l'élément de liste

  1. À la fin du bloc Row de la fonction modulable DogItem(), ajoutez un Spacer. Transmettez un Modifier avec weight(1f). Modifier.weight() permet à l'espace vide de remplir l'espace disponible restant dans la ligne.
Row(
   modifier = Modifier
       .fillMaxWidth()
       .padding(8.dp)
) {
   DogIcon(dog.imageResourceId)
   DogInformation(dog.name, dog.age)
   Spacer(Modifier.weight(1f))
   DogItemButton(
      expanded = expanded,
      onClick = { }
   )
}
  1. Appuyez sur Build & Refresh (Compiler et actualiser) pour actualiser l'aperçu dans le volet Design (Conception). Notez que le bouton "Développer" est désormais aligné au bout de l'élément de liste.

f6a140413de9ad54.png

5. Ajouter un composable pour afficher le passe-temps

Dans cette tâche, vous ajouterez des composables Text afin d'afficher des informations sur les loisirs du chien.

66ea5cc5c7253d55.png

  1. Créez une fonction modulable appelée DogHobby(), qui récupère l'ID de ressource de chaîne pour le passe-temps d'un chien ainsi qu'un Modifier facultatif.
  2. Dans la fonction DogHobby(), créez une colonne avec les attributs de marge intérieure suivants pour ajouter de l'espace entre la colonne et les composables enfants.
import androidx.annotation.StringRes

@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
   Column(
       modifier = modifier.padding(
           start = 16.dp,
            top = 8.dp,
            bottom = 16.dp,
            end = 16.dp
       )
   ) { }
}
  1. Dans le bloc de colonne, ajoutez deux composables Text : un pour afficher le texte About (Passe-temps) au-dessus des informations sur le passe-temps préféré, et l'autre pour afficher les informations en question.

3051387c4b9c7455.png

  1. Pour le texte About, définissez le style sur h3 (titre de niveau 3) et la couleur sur onBackground. Pour les informations de passe-temps, définissez le style sur body1.
Column(
   modifier = modifier.padding(
       //..
   )
) {
   Text(
       text = stringResource(R.string.about),
       style = MaterialTheme.typography.h3,
   )
   Text(
       text = stringResource(dogHobby),
       style = MaterialTheme.typography.body1,
   )
}
  1. Voici à quoi ressemble la fonction modulable DogHobby() après ces modifications.
@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
   Column(
       modifier = modifier.padding(
           start = 16.dp,
           top = 8.dp,
           bottom = 16.dp,
           end = 16.dp
       )
   ) {
       Text(
           text = stringResource(R.string.about),
           style = MaterialTheme.typography.h3
       )
       Text(
           text = stringResource(dogHobby),
           style = MaterialTheme.typography.body1
       )
   }
}
  1. Pour afficher le composable DogHobby(), dans DogItem(), encapsulez la Row avec une Column. Appelez la fonction DogHobby() en transmettant le paramètre dog.hobbies après la Row en tant que deuxième enfant.
Column() {
   Row(
       //..
   ) {
       //..
   }
   DogHobby(dog.hobbies)
}

Une fois terminée, la fonction DogItem() doit se présenter comme suit :

@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   var expanded by remember { mutableStateOf(false) }
   Card(
        elevation = 4.dp,
       modifier = modifier.padding(8.dp)
   ) {
       Column() {
           Row(
               modifier = Modifier
                   .fillMaxWidth()
                   .padding(8.dp)
           ) {
               DogIcon(dog.imageResourceId)
               DogInformation(dog.name, dog.age)
               Spacer(Modifier.weight(1f))
               DogItemButton(
                   expanded = expanded,
                   onClick = { expanded = !expanded },
               )
           }
           DogHobby(dog.hobbies)
       }
   }
}
  1. Appuyez sur Build & Refresh (Compiler et actualiser) pour actualiser l'aperçu dans le volet Design (Conception). Le passe-temps des chiens s'affiche.

9e2e68a4bc4a8ae1.png

6. Afficher ou masquer le passe-temps lorsque l'utilisateur appuie sur le bouton

Votre application dispose d'un bouton "Développer" pour chaque élément de la liste, mais ce bouton n'a aucun effet pour l'instant. Dans cette section, vous ajouterez la possibilité d'afficher ou masquer les informations concernant le passe-temps lorsque l'utilisateur appuie sur le bouton "Développer".

  1. Accédez à la fonction modulable DogItem(). Dans l'appel de fonction DogItemButton(), définissez l'expression lambda onClick() et appliquez la valeur d'état booléenne true pour expanded lorsque l'utilisateur appuie sur le bouton, et rétablissez false lorsque l'utilisateur appuie de nouveau.
DogItemButton(
   expanded = expanded,
   onClick = { expanded = !expanded }
)
  1. Dans la fonction DogItemButton(), encapsulez l'appel de fonction DogHobby() avec une vérification if sur la valeur booléenne expanded.
// No need to copy over
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   var expanded by remember { mutableStateOf(false) }
   Card(
       //..
   ) {
       Column() {
           Row(
               //..
           ) {
               //..
           }
           if (expanded) {
               DogHobby(dog.hobbies)
           }
       }
   }
}

Dans le code ci-dessus, les informations de passe-temps du chien ne s'affichent que si la valeur de expanded est true.

  1. L'aperçu vous permet de voir à quoi ressemble l'interface utilisateur et d'interagir avec. Pour interagir avec l'aperçu de l'interface utilisateur, activez le mode interactif en cliquant sur le bouton 42379dbe94a7a497.png en haut à droite du volet Design. L'aperçu passe en mode interactif.

2a4ad1f3d2d0bff7.png

  1. Interagissez avec l'aperçu en cliquant sur le bouton "Développer". Notez que les informations du passe-temps du chien sont affichées ou masquées lorsque vous cliquez sur le bouton.

6ee6774b5b14c7e1.gif

Notez que l'icône du bouton "Développer" reste la même lorsque l'élément de liste est développé. Pour offrir une meilleure expérience utilisateur, vous devez modifier l'icône afin que ExpandMore affiche la flèche vers le bas c761ef298c2aea5a.png, et ExpandLess affiche la flèche vers le haut b380f933be0b6ff4.png.

  1. Dans la fonction DogItemButton(), modifiez la valeur imageVector en fonction de l'état expanded, comme suit :
import androidx.compose.material.icons.filled.ExpandLess

@Composable
private fun DogItemButton(
   //..
) {
   IconButton(onClick = onClick) {
       Icon(
           imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
           //..
       )
   }
}
  1. Exécutez l'application sur un appareil ou un émulateur, ou passez à nouveau en mode interactif dans l'aperçu. Notez que l'icône alterne entre ExpandMore  c761ef298c2aea5a.png et ExpandLess  b380f933be0b6ff4.png.

bf8bb280a774a6d4.gif

Bravo ! Vous avez mis à jour votre icône.

Avez-vous noté le changement abrupt de hauteur lorsque vous développez un élément de la liste ? Dans une application, ce type de changements brusques ne reflète pas une finition soignée. Pour résoudre ce problème, vous allez ajouter une animation à votre application.

7. Ajouter une animation

Les animations peuvent ajouter des repères visuels pour informer les utilisateurs de ce qui se passe dans votre application. Ils sont particulièrement utiles lorsque l'interface utilisateur change d'état (par exemple lorsque de nouveaux contenus se chargent ou que de nouvelles actions deviennent disponibles). Les animations permettent également d'améliorer l'aspect général de l'application.

Dans cette section, vous ajouterez une animation de rétroaction au changement de hauteur de l'élément de liste.

Animation de rétroaction

Une animation de rétroaction s'inspire des lois de la physique, et plus spécifiquement de la force de tension. Avec une animation de rétroaction, la valeur et la vitesse du mouvement sont calculées en fonction de la tension appliquée.

Par exemple, si vous faites glisser une icône d'application sur l'écran, puis la relâchez en relevant votre doigt, l'icône revient à son emplacement d'origine sous l'effet d'une force invisible.

L'animation suivante illustre l'effet de rétroaction. Lorsque l'icône est relâchée, elle revient à sa place, comme poussée par un ressort.

7b52f63dc639c28d.gif

Effet de rétroaction

La force de tension ("effet ressort") est déterminée par les deux propriétés suivantes :

  • Taux d'amortissement : la résistance à la déformation, qui limite les rebonds du ressort.
  • Niveau de raideur : la résistance à l'étirement, qui définit la vitesse du ressort en fin de course.

Vous trouverez ci-dessous quelques exemples d'animations avec différents paramètres d'amortissement et de raideur.

Effet de rétroactionRebond élevé

Effet de rétroactionAucun rebond

Raideur élevée

Raideur très faible

Ajoutons maintenant une animation de rétroaction à notre application.

  1. Accédez à MainActivity.kt. Dans DogItem(), ajoutez un paramètre modifier à la mise en page Column.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   Card(
       //..
   ) {
       Column(
          modifier = Modifier
       ){
           //..
       }
   }
}

Observez l'appel de fonction DogHobby() dans la fonction modulable DogItem(). Les informations concernant le passe-temps du chien sont incluses dans la composition, en fonction de la valeur booléenne expanded. La hauteur de l'élément de liste change, selon que les informations de passe-temps sont affichées ou masquées. Vous utiliserez le modificateur animateContentSize pour ajouter une transition entre la nouvelle et l'ancienne hauteur.

// No need to copy over
@Composable
fun DogItem(...) {

        //..
           if (expanded) {
               DogHobby(dog.hobbies)
           }
}
  1. Enchaînez ce modificateur au modificateur animateContentSize pour animer le changement de dimensions (hauteur de l'élément de liste).
import androidx.compose.animation.animateContentSize

Column(
           modifier = Modifier
               .animateContentSize()
       ) {
            //..
       }

L'implémentation actuelle anime la hauteur de l'élément de liste dans votre application. Cependant, l'animation est si subtile qu'il est difficile de la discerner lorsque vous exécutez l'application. Pour résoudre ce problème, vous pouvez utiliser le paramètre facultatif animationSpec, qui permet de personnaliser l'animation.

  1. Ajoutez le paramètre animationSpec à l'appel de fonction animateContentSize(). Définissez-le sur une animation de rétroaction (spring) avec les paramètres DampingRatioMediumBouncy et StiffnessLow.
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring

Column(
   modifier = Modifier
       .animateContentSize(
           animationSpec = spring(
               dampingRatio = Spring.DampingRatioMediumBouncy,
               stiffness = Spring.StiffnessLow
           )
       )
)
  1. Appuyez sur Build & Refresh (Compiler et actualiser) pour actualiser l'aperçu dans le volet Design (Conception). Ensuite, passez en mode interactif, ou exécutez votre application sur un émulateur ou un appareil pour voir votre animation en action.

8cf711b8821b4696.gif

Exécutez à nouveau l'application sur votre émulateur ou appareil, et appréciez vos belles animations !

1e9cf1dbc490924a.gif

8. Expérimenter avec d'autres animations (facultatif)

animate*AsState

Les fonctions animate*AsState() comptent parmi les API d'animation les plus simples dans Compose. Elles permettent d'animer une seule valeur. Vous fournissez uniquement la valeur finale (ou la valeur cible), et l'API lance une animation menant de la valeur actuelle à la valeur spécifiée.

Compose fournit des fonctions animate*AsState() pour Float, Color, Dp, Size, Offset et Int, entre autres. Vous pouvez facilement prendre en charge d'autres types de données en utilisant animateValueAsState(), qui utilise un type générique.

Utilisez la fonction animateColorAsState() pour animer la couleur lorsqu'un élément de la liste est développé.

Astuce :

  1. Déclarez une couleur et déléguez son initialisation à la fonction animateColorAsState().
  2. Définissez le paramètre nommé targetValue en fonction de la valeur booléenne expanded.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   val color by animateColorAsState(
       targetValue = if (expanded) Green25 else MaterialTheme.colors.surface,
   )
   Card(
       //..
   ) {...}
}
  1. Définissez la color déclaré ci-dessus comme modificateur d'arrière-plan pour la Column.
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
   //..
   Card(
       //..
   ) {
       Column(
           modifier = Modifier
               .animateContentSize(
                   //..
                   )
               )
               .background(color = color)
       ) {...}
}

9. Télécharger le code de solution

Pour télécharger le code de l'atelier de programmation terminé, utilisez la commande Git suivante :

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git

Vous pouvez également télécharger le dépôt sous forme de fichier ZIP, le décompresser et l'ouvrir dans Android Studio.

(Télécharger le ZIP)

Si vous souhaitez voir le code de solution, affichez-le sur GitHub.

10. Conclusion

Félicitations ! Vous avez ajouté un bouton pour afficher et masquer les informations concernant les chiens. Vous avez amélioré l'expérience utilisateur grâce aux animations de rétroaction. Vous avez également appris à utiliser le mode interactif dans le volet de conception.

Vous pouvez également explorer un autre type d'animation Jetpack Compose. N'oubliez pas de partager le fruit de vos efforts sur les réseaux sociaux avec le hashtag #AndroidBasics.

En savoir plus