Android Kotlin Fundamentals 05.3: liaison de données avec ViewModel et LiveData

Cet atelier de programmation fait partie du cours Android Kotlin Fundamentals. Vous tirerez le meilleur parti de ce cours si vous suivez les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont répertoriés sur la page de destination des ateliers de programmation Android Kotlin Fundamentals.

Introduction

Dans les précédents ateliers de programmation de cette leçon, vous avez amélioré le code de l'application GuessTheWord. L'application utilise désormais des objets ViewModel. Les données de l'application survivent donc aux modifications de la configuration de l'appareil, telles que les rotations d'écran et les modifications apportées à la disponibilité du clavier. Vous avez également ajouté le LiveData observable, afin que les vues soient automatiquement averties lorsque les données observées changent.

Dans cet atelier de programmation, vous continuez à travailler avec l'application GuessTheWord. Vous liez les vues aux classes ViewModel de l'application pour que les vues de votre mise en page communiquent directement avec les objets ViewModel. Jusqu'à présent, les vues étaient indirectement transmises aux éléments ViewModel via les fragments d'application. Après avoir intégré la liaison de données avec les objets ViewModel, vous n'avez plus besoin de gestionnaires de clics dans les fragments de l'application. Vous les supprimez donc.

Vous allez également modifier l'application GuessTheWord pour utiliser LiveData comme source de liaison de données afin d'informer l'UI des modifications apportées aux données, sans utiliser les méthodes d'observation LiveData.

Ce que vous devez déjà savoir

  • Créer des applications Android de base en Kotlin
  • Fonctionnement des cycles de vie des activités et des fragments.
  • Comment utiliser des objets ViewModel dans votre application
  • Comment stocker des données en utilisant LiveData dans une ViewModel
  • Découvrez comment ajouter des méthodes d'observation afin d'observer les modifications dans les données LiveData.

Points abordés

  • Utiliser des éléments de la bibliothèque de liaisons de données
  • Intégrer ViewModel avec une liaison de données
  • Intégrer LiveData avec une liaison de données
  • Utiliser des liaisons d'écouteur pour remplacer les écouteurs de clics dans un fragment.
  • Comment ajouter une mise en forme de chaîne à des expressions de liaison de données.

Objectifs de l'atelier

  • Les vues des mises en page GuessTheWord communiquent indirectement avec des objets ViewModel, en utilisant des contrôleurs d'interface utilisateur (fragments) pour transmettre des informations. Dans cet atelier de programmation, vous associez les vues de l'application aux objets ViewModel pour qu'elles communiquent directement avec les objets ViewModel.
  • Vous modifiez l'application pour utiliser LiveData comme source de liaison de données. Après cette modification, les objets LiveData notifient l'interface utilisateur des modifications apportées aux données, et les méthodes d'observation LiveData ne sont plus nécessaires.

Dans les ateliers de programmation de la leçon 5, vous allez développer l'application GuessTheWord en commençant par le code de démarrage. GuessTheWord est un jeu de charades à deux joueurs, où les joueurs collaborent pour obtenir le meilleur score possible.

Le premier joueur analyse les mots dans l'application et les joue tous à tour de rôle. Veillez donc à ne pas afficher le mot au deuxième joueur. Le deuxième joueur essaie de deviner le mot.

Pour lancer ce jeu, le premier joueur ouvre l'application sur l'appareil et voit un mot, par exemple "guitare", comme illustré dans la capture d'écran ci-dessous.

Le premier joueur joue le mot, en prenant soin de ne pas le prononcer.

  • Lorsque le deuxième joueur essaie de deviner le mot, le premier appuie sur le bouton OK, ce qui augmente le nombre de points et affiche le mot suivant.
  • S'il ne parvient pas à deviner le mot, le premier joueur appuie sur le bouton Ignorer, ce qui diminue le nombre de fois jusqu'à l'affichage du mot suivant.
  • Pour terminer la partie, appuyez sur le bouton Terminer le jeu. (Cette fonctionnalité n'est pas disponible dans le code de démarrage du premier atelier de programmation de cette série.)

Dans cet atelier de programmation, vous allez améliorer l'application GuessTheWord en intégrant la liaison de données à LiveData dans les objets ViewModel. Cela permet d'automatiser la communication entre les vues dans la mise en page et les objets ViewModel, et de simplifier le code en utilisant LiveData.

Écran titre

Écran de jeu

Écran de score

Dans cette tâche, vous allez localiser et exécuter le code de démarrage de cet atelier de programmation. Vous pouvez utiliser l'application GuessTheWord que vous avez créée dans l'atelier de programmation précédent comme code de démarrage. Vous pouvez également en télécharger une.

  1. (Facultatif) Si vous n'utilisez pas le code de l'atelier de programmation précédent, téléchargez-le. Décompressez le code, puis ouvrez le projet dans Android Studio.
  2. Exécutez l'application et jouez au jeu.
  3. Notez que le bouton OK affiche le mot suivant et augmente le score de 1, tandis que le bouton Ignorer affiche le mot suivant et diminue le score de 1. Le bouton Terminer le jeu met fin au jeu.
  4. Parcourez tous les mots et notez que l'application accède automatiquement à l'écran de score.

Dans un atelier de programmation précédent, vous avez utilisé la liaison de données pour accéder aux vues dans l'application GuessTheWord en toute sécurité. Mais la véritable force de ce composant est de faire ce que suggère le nom: lier des données directement aux objets de la vue dans votre application.

Architecture d'application actuelle

Dans votre application, les vues sont définies dans la mise en page XML, et les données correspondantes sont conservées dans les objets ViewModel. Entre chaque vue et les ViewModel correspondantes, un contrôleur d'interface utilisateur agit comme un relais entre elles.

Exemple :

  • Le bouton OK est défini en tant que vue Button dans le fichier de mise en page game_fragment.xml.
  • Lorsque l'utilisateur appuie sur le bouton OK, un écouteur de clics du fragment GameFragment appelle l'écouteur de clics correspondant dans GameViewModel.
  • Le score est mis à jour dans GameViewModel.

La vue Button et la GameViewModel ne communiquent pas directement. Ils doivent utiliser l'écouteur de clics GameFragment.

ViewModel transmis dans la liaison de données

Il serait plus simple si les vues de la mise en page communiquent directement avec les données des objets ViewModel, sans utiliser les contrôleurs de l'interface utilisateur comme intermédiaires.

Les objets ViewModel contiennent toutes les données de l'interface utilisateur de l'application GuessTheWord. En transmettant des objets ViewModel à la liaison de données, vous pouvez automatiser certaines des communications entre les vues et les objets ViewModel.

Dans cette tâche, vous allez associer les classes GameViewModel et ScoreViewModel à leurs mises en page XML correspondantes. Vous allez également configurer des liaisons d'écouteur pour gérer les événements de clic.

Étape 1: Ajoutez une liaison de données pour GameViewModel

Dans cette étape, vous allez associer GameViewModel au fichier de mise en page correspondant (game_fragment.xml).

  1. Dans le fichier game_fragment.xml, ajoutez une variable de liaison de données de type GameViewModel. Si vous rencontrez des erreurs dans Android Studio, nettoyez et recréez le projet.
<layout ...>

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  
   <androidx.constraintlayout...
  1. Dans le fichier GameFragment, transmettez GameViewModel à la liaison de données.

    Pour ce faire, attribuez viewModel à la variable binding.gameViewModel que vous avez déclarée à l'étape précédente. Placez ce code dans onCreateView(), après l'initialisation de viewModel. Si vous rencontrez des erreurs dans Android Studio, nettoyez et recréez le projet.
// Set the viewmodel for databinding - this allows the bound layout access 
// to all the data in the ViewModel
binding.gameViewModel = viewModel

Étape 2: Utilisez des liaisons d'écouteur pour la gestion des événements

Les liaisons d'écouteur sont des expressions de liaison qui s'exécutent lorsque des événements tels que onClick(), onZoomIn() ou onZoomOut() sont déclenchés. Les liaisons d'écouteur sont écrites sous forme d'expressions lambda.

La liaison de données crée un écouteur et définit l'écouteur sur la vue. Lorsque l'événement écouté est écouté, l'écouteur évalue l'expression lambda. Les liaisons d'écouteur fonctionnent avec le plug-in Android Gradle 2.0 ou version ultérieure. Pour en savoir plus, consultez l'article Mises en page et expressions liées à la liaison.

Au cours de cette étape, vous allez remplacer les écouteurs de clics du fichier GameFragment par des liaisons d'écouteurs dans le fichier game_fragment.xml.

  1. Dans game_fragment.xml, ajoutez l'attribut onClick au skip_button. Définissez une expression de liaison et appelez la méthode onSkip() dans GameViewModel. Cette expression de liaison est appelée liaison d'écouteur.
<Button
   android:id="@+id/skip_button"
   ...
   android:onClick="@{() -> gameViewModel.onSkip()}"
   ... />
  1. De même, liez l'événement de clic de la correct_button à la méthode onCorrect() dans le GameViewModel.
<Button
   android:id="@+id/correct_button"
   ...
   android:onClick="@{() -> gameViewModel.onCorrect()}"
   ... />
  1. Associez l'événement de clic de la end_game_button à la méthode onGameFinish() dans le GameViewModel.
<Button
   android:id="@+id/end_game_button"
   ...
   android:onClick="@{() -> gameViewModel.onGameFinish()}"
   ... />
  1. Dans GameFragment, supprimez les instructions qui définissent les écouteurs de clics, ainsi que les fonctions que les écouteurs de clics appellent. Vous n'en avez plus besoin.

Code à supprimer:

binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }

/** Methods for buttons presses **/
private fun onSkip() {
   viewModel.onSkip()
}
private fun onCorrect() {
   viewModel.onCorrect()
}
private fun onEndGame() {
   gameFinished()
}

Étape 3: Ajoutez une liaison de données pour le ScoreViewModel

Dans cette étape, vous allez associer ScoreViewModel au fichier de mise en page correspondant (score_fragment.xml).

  1. Dans le fichier score_fragment.xml, ajoutez une variable de liaison de type ScoreViewModel. Cette étape est semblable à celle que vous avez suivie pour GameViewModel ci-dessus.
<layout ...>
   <data>
       <variable
           name="scoreViewModel"
           type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
  1. Dans score_fragment.xml, ajoutez l'attribut onClick au play_again_button. Définissez une liaison d'écouteur et appelez la méthode onPlayAgain() dans ScoreViewModel.
<Button
   android:id="@+id/play_again_button"
   ...
   android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
   ... />
  1. Dans ScoreFragment, initialisez viewModel dans onCreateView(). Ensuite, initialisez la variable de liaison binding.scoreViewModel.
viewModel = ...
binding.scoreViewModel = viewModel
  1. Dans ScoreFragment, supprimez le code qui définit l'écouteur de clics pour playAgainButton. Si Android Studio affiche une erreur, nettoyez et recréez le projet.

Code à supprimer:

binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. Exécutez votre application. Celle-ci devrait fonctionner comme avant, mais les vues du bouton doivent désormais communiquer directement avec les objets ViewModel. Les vues ne communiquent plus via les gestionnaires de clics sur le bouton dans ScoreFragment.

Résoudre les messages d'erreur liés à la liaison de données

Lorsqu'une application utilise la liaison de données, le processus de compilation génère des classes intermédiaires qui sont utilisées pour la liaison de données. Une application peut rencontrer des erreurs qu'Android Studio ne détecte pas tant que vous ne l'avez pas compilée. De ce fait, aucun avertissement ou code rouge ne s'affiche lorsque vous écrivez le code. Toutefois, au moment de la compilation, vous obtenez des erreurs cryptées provenant des classes intermédiaires générées.

Si vous recevez un message d'erreur crypté:

  1. Lisez attentivement le message dans le volet Build d'Android Studio. Si vous constatez qu'un emplacement se termine par databinding, une erreur s'est produite au niveau de la liaison de données.
  2. Dans le fichier XML de mise en page, recherchez les erreurs d'attributs onClick qui utilisent la liaison de données. Recherchez la fonction appelée par l'expression lambda et assurez-vous qu'elle existe.
  3. Dans la section <data> du code XML, vérifiez l'orthographe de la variable data-binding.

Par exemple, notez la faute d'orthographe dans le nom de la fonction onCorrect() dans la valeur d'attribut suivante:

android:onClick="@{() -> gameViewModel.onCorrectx()}"

Notez également l'orthographe de gameViewModel dans la section <data> du fichier XML :

<data>
   <variable
       name="gameViewModelx"
       type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>

Android Studio ne détecte pas ces erreurs tant que vous n'avez pas compilé l'application. Le compilateur affiche ensuite un message d'erreur semblable à celui-ci :

error: cannot find symbol
import com.example.android.guesstheword.databinding.GameFragmentBindingImpl"

symbol:   class GameFragmentBindingImpl
location: package com.example.android.guesstheword.databinding

La liaison de données fonctionne bien avec LiveData qui est utilisé avec des objets ViewModel. Maintenant que vous avez ajouté une liaison de données aux objets ViewModel, vous pouvez intégrer LiveData.

Dans cette tâche, vous allez modifier l'application GuessTheWord pour utiliser LiveData comme source de liaison de données afin d'informer l'UI des modifications apportées aux données, sans avoir à utiliser les méthodes d'observation LiveData.

Étape 1: Ajoutez le mot "LiveData" au fichier game_fragment.xml

Au cours de cette étape, vous allez lier la vue textuelle actuelle directement à l'objet LiveData dans ViewModel.

  1. Dans game_fragment.xml, ajoutez l'attribut android:text à la vue Texte word_text.

Définissez-le sur l'objet LiveData, word à partir de GameViewModel, en utilisant la variable de liaison gameViewModel.

<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{gameViewModel.word}"
   ... />

Notez que vous n'avez pas besoin d'utiliser word.value. Vous pouvez utiliser l'objet LiveData réel. L'objet LiveData affiche la valeur actuelle de word. Si la valeur de word est nulle, l'objet LiveData affiche une chaîne vide.

  1. Dans GameFragment, dans onCreateView(), après avoir initialisé gameViewModel, définissez l'activité actuelle en tant que propriétaire du cycle de vie de la variable binding. Cela définit le champ d'application de l'objet LiveData ci-dessus, qui permet à l'objet de mettre à jour automatiquement les vues dans la mise en page game_fragment.xml.
binding.gameViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
  1. Dans GameFragment, supprimez l'observateur pour le LiveData word.

Code à supprimer:

/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})
  1. Exécutez votre application et jouez au jeu. Le mot actuel est en cours de mise à jour sans méthode d'observation dans le contrôleur d'interface utilisateur.

Étape 2: Ajoutez LiveData au fichier score_fragment.xml

Au cours de cette étape, vous allez associer LiveData score à la vue textuelle du score dans le fragment de score.

  1. Dans score_fragment.xml, ajoutez l'attribut android:text à la vue textuelle du score. Attribuez la valeur scoreViewModel.score à l'attribut text. Comme score est un entier, convertissez-le en chaîne à l'aide de String.valueOf().
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{String.valueOf(scoreViewModel.score)}"
   ... />
  1. Dans ScoreFragment, après avoir initialisé scoreViewModel, définissez l'activité actuelle en tant que propriétaire du cycle de vie de la variable binding.
binding.scoreViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
  1. Dans ScoreFragment, supprimez l'observateur de l'objet score.

Code à supprimer:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Exécutez votre application et jouez au jeu. Notez que le score dans le fragment de score est affiché correctement, sans observateur dans le fragment de score.

Étape 3: Ajoutez une mise en forme de chaîne avec la liaison de données

Dans la mise en page, vous pouvez ajouter une mise en forme de chaîne avec une liaison de données. Dans cette tâche, vous allez formater le mot actuel pour y ajouter des guillemets. Vous devez également formater la chaîne de score comme préfixe Score Score, comme illustré ci-dessous.

  1. Dans string.xml, ajoutez les chaînes suivantes, que vous utiliserez pour mettre en forme les vues textuelles word et score. %s et %d sont les espaces réservés au mot actuel et au score actuel.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
  1. Dans game_fragment.xml, mettez à jour l'attribut text de la vue Texte word_text pour utiliser la ressource de chaîne quote_format. Transmettre dans gameViewModel.word. Le mot actuel est transmis en tant qu'argument à la chaîne de mise en forme.
<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{@string/quote_format(gameViewModel.word)}"
   ... />
  1. Mettez en forme la vue textuelle de score de la même manière que word_text. Dans game_fragment.xml, ajoutez l'attribut text à la vue Texte score_text. Utilisez la ressource de chaîne score_format, qui utilise un argument numérique, représenté par l'espace réservé %d. Transmettez l'objet LiveData, score, en tant qu'argument à cette chaîne de mise en forme.
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{@string/score_format(gameViewModel.score)}"
   ... />
  1. Dans la classe GameFragment, supprimez le code observateur score de la méthode onCreateView().

Code à supprimer:

viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Nettoyez, recréez et exécutez votre application, puis jouez au jeu. Notez que le mot actuel et le score sont mis en forme dans l'écran du jeu.

Félicitations ! Vous avez intégré LiveData et ViewModel avec la liaison de données dans votre application. Cela permet aux vues de votre mise en page de communiquer directement avec le ViewModel, sans utiliser de gestionnaires de clics dans le fragment. Vous avez également utilisé les objets LiveData comme source de liaison de données pour informer automatiquement l'UI des modifications apportées aux données, sans les méthodes d'observation LiveData.

Projet Android Studio: GuessTheWord

  • La bibliothèque de liaisons de données fonctionne de manière transparente avec les composants d'architecture Android, tels que ViewModel et LiveData.
  • Les mises en page de votre application peuvent être associées aux données des composants d'architecture, ce qui vous permet déjà de gérer le cycle de vie du contrôleur d'interface et d'être informé des modifications apportées aux données.

Liaison de données ViewModel

  • Vous pouvez associer un ViewModel à une mise en page à l'aide d'une liaison de données.
  • Les objets ViewModel contiennent les données d'interface utilisateur. En transmettant des objets ViewModel dans la liaison de données, vous pouvez automatiser certaines des communications entre les vues et les objets ViewModel.

Pour associer un ViewModel à une mise en page:

  • Dans le fichier de mise en page, ajoutez une variable de liaison de données de type ViewModel.
   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  • Dans le fichier GameFragment, transmettez GameViewModel à la liaison de données.
binding.gameViewModel = viewModel

Liaisons d'écouteurs

  • Les liaisons d'écouteur sont des expressions de liaison qui s'exécutent lorsque des événements de clic tels que onClick() sont déclenchés.
  • Les liaisons d'écouteur sont écrites sous forme d'expressions lambda.
  • À l'aide de liaisons d'écouteur, vous remplacez les écouteurs de clics dans les contrôleurs d'interface par les liaisons d'écouteur dans le fichier de mise en page.
  • La liaison de données crée un écouteur et définit l'écouteur sur la vue.
 android:onClick="@{() -> gameViewModel.onSkip()}"

Ajouter LiveData à la liaison de données

  • Les objets LiveData peuvent être utilisés comme source de liaison de données pour informer automatiquement l'UI des modifications apportées aux données.
  • Vous pouvez lier la vue directement à l'objet LiveData dans le ViewModel. Lorsque LiveData dans ViewModel change, les vues de la mise en page peuvent être automatiquement mises à jour, sans les méthodes d'observation dans les contrôleurs de l'interface utilisateur.
android:text="@{gameViewModel.word}"
  • Pour que la liaison de données LiveData fonctionne, définissez l'activité actuelle (contrôleur d'interface utilisateur) en tant que propriétaire du cycle de vie de la variable binding dans le contrôleur d'interface utilisateur.
binding.lifecycleOwner = this

Mise en forme de chaîne avec liaison de données

  • La liaison de données vous permet de mettre en forme une ressource de chaîne avec des espaces réservés tels que %s pour les chaînes et %d pour des entiers.
  • Pour mettre à jour l'attribut text de la vue, transmettez l'objet LiveData en tant qu'argument à la chaîne de mise en forme.
 android:text="@{@string/quote_format(gameViewModel.word)}"

Cours Udacity:

Documentation pour les développeurs Android:

Cette section répertorie les devoirs possibles pour les élèves qui effectuent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. C'est à l'enseignant de procéder comme suit:

  • Si nécessaire, rendez-les.
  • Communiquez aux élèves comment rendre leurs devoirs.
  • Notez les devoirs.

Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ils n'ont pas besoin d'attribuer les devoirs de leur choix.

Si vous suivez vous-même cet atelier de programmation, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.

Répondez à ces questions.

Question 1

Parmi les affirmations suivantes concernant les liaisons d'écouteur, laquelle n'est pas vraie ?

  • Les liaisons d'écouteur sont des expressions de liaison qui s'exécutent lorsqu'un événement se produit.
  • Les liaisons d'écouteur fonctionnent avec toutes les versions du plug-in Android Gradle.
  • Les liaisons d'écouteur sont écrites sous forme d'expressions lambda.
  • Les liaisons d'écouteur sont semblables aux références de méthode, mais elles vous permettent d'exécuter des expressions de liaison de données arbitraires.

Question 2

Supposons que votre application inclut la ressource de chaîne suivante :
<string name="generic_name">Hello %s</string>

Quelle syntaxe correspond à la mise en forme de la chaîne à l'aide de l'expression data-binding ?

  • android:text= "@{@string/generic_name(user.name)}"
  • android:text= "@{string/generic_name(user.name)}"
  • android:text= "@{@generic_name(user.name)}"
  • android:text= "@{@string/generic_name,user.name}"

Question 3

Quand une expression "binding-binding" est-elle évaluée et exécutée ?

  • Lorsque les données soumises à une LiveData sont modifiées
  • Lorsqu'une activité est recréée par une modification de configuration
  • Lorsqu'un événement tel que onClick() se produit
  • Lorsque l'activité passe en arrière-plan

Démarrez la leçon suivante : 5.4: Transformations LiveData.

Pour obtenir des liens vers d'autres ateliers de programmation dans ce cours, consultez la page de destination des ateliers de programmation Android Kotlin Fundamentals.