Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.
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
, ce qui permet aux données de l'application de survivre aux modifications de la configuration de l'appareil, telles que les rotations d'écran et les modifications de la disponibilité du clavier. Vous avez également ajouté LiveData
observable, de sorte que les vues sont automatiquement averties lorsque les données observées changent.
Dans cet atelier de programmation, vous allez continuer à travailler avec l'application GuessTheWord. Vous allez lier des vues aux classes ViewModel
de l'application afin que les vues de votre mise en page communiquent directement avec les objets ViewModel
. (Jusqu'à présent, les vues de votre application communiquaient indirectement avec ViewModel
, par le biais des fragments de l'application.) Une fois que vous avez intégré la liaison de données aux objets ViewModel
, vous n'avez plus besoin de gestionnaires de clics dans les fragments de l'application. Vous pouvez donc les supprimer.
Vous modifiez également l'application GuessTheWord pour qu'elle utilise 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
- Vous savez comment créer des applications Android de base en langage Kotlin.
- Vous comprenez le fonctionnement des cycles de vie des activités et des fragments.
- Comment utiliser les objets
ViewModel
dans votre application. - Comment stocker des données à l'aide de
LiveData
dans unViewModel
. - Comment ajouter des méthodes d'observation pour voir les modifications apportées aux données
LiveData
.
Points abordés
- Utiliser les éléments de la bibliothèque Data Binding.
- Comment intégrer
ViewModel
avec la liaison de données. - Comment intégrer
LiveData
avec la liaison de données. - Utiliser les liaisons d'écouteur pour remplacer les écouteurs de clics dans un fragment.
- Comment ajouter la mise en forme de chaînes aux expressions de liaison de données.
Objectifs de l'atelier
- Les vues des mises en page GuessTheWord communiquent indirectement avec les objets
ViewModel
, en utilisant des contrôleurs d'UI (fragments) pour transmettre les informations. Dans cet atelier de programmation, vous allez lier les vues de l'application aux objetsViewModel
afin que les vues communiquent directement avec les objetsViewModel
. - Vous modifiez l'application pour qu'elle utilise
LiveData
comme source de liaison de données. Après ce changement, les objetsLiveData
informent l'UI des modifications apportées aux données, et les méthodes d'observationLiveData
ne sont plus nécessaires.
Dans les ateliers de programmation de la leçon 5, vous développez l'application GuessTheWord, en commençant par le code de démarrage. GuessTheWord est un jeu de charades à deux joueurs, dans lequel les joueurs collaborent pour obtenir le meilleur score possible.
Le premier joueur regarde les mots dans l'application et mime chacun d'eux à tour de rôle, en veillant à ne pas les montrer au deuxième joueur. Le deuxième joueur essaie de deviner le mot.
Pour jouer, le premier joueur ouvre l'application sur l'appareil et voit un mot, par exemple "guitare", comme indiqué sur la capture d'écran ci-dessous.
Le premier joueur mime le mot, en veillant à ne pas le prononcer.
- Lorsque le deuxième joueur devine le mot, le premier joueur appuie sur le bouton OK, ce qui incrémente le compteur et affiche le mot suivant.
- Si le deuxième joueur ne parvient pas à deviner le mot, le premier joueur appuie sur le bouton Passer, ce qui diminue le nombre de mots d'un et passe au mot suivant.
- Pour mettre fin à la partie, appuyez sur le bouton End Game (Mettre fin à la partie). (Cette fonctionnalité n'est pas incluse dans le code de démarrage du premier atelier de programmation de la série.)
Dans cet atelier de programmation, vous allez améliorer l'application GuessTheWord en intégrant la liaison de données avec LiveData
dans les objets ViewModel
. Cela automatise la communication entre les vues de la mise en page et les objets ViewModel
, et vous permet de simplifier votre 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, ou télécharger une application de démarrage.
- (Facultatif) Si vous n'utilisez pas votre code de l'atelier de programmation précédent, téléchargez le code de démarrage pour cet atelier de programmation. Décompressez le code, puis ouvrez le projet dans Android Studio.
- Exécutez l'application et jouez.
- Notez que le bouton OK affiche le mot suivant et augmente le score de un, tandis que le bouton Ignorer affiche le mot suivant et diminue le score de un. Le bouton End Game (Terminer la partie) met fin à la partie.
- Parcourez tous les mots et remarquez que l'application accède automatiquement à l'écran du score.
Dans un précédent atelier de programmation, vous avez utilisé la liaison de données comme moyen sûr d'accéder aux vues dans l'application GuessTheWord. Mais la véritable puissance de la liaison de données réside dans ce que son nom suggère : lier les données directement aux objets de vue de votre application.
Architecture actuelle de l'application
Dans votre application, les vues sont définies dans la mise en page XML, et les données de ces vues sont conservées dans des objets ViewModel
. Entre chaque vue et son ViewModel
correspondant se trouve un contrôleur d'UI, qui sert de relais entre eux.
Exemple :
- Le bouton OK est défini comme une vue
Button
dans le fichier de mise en pagegame_fragment.xml
. - Lorsque l'utilisateur appuie sur le bouton Got It (OK), un écouteur de clics dans le fragment
GameFragment
appelle l'écouteur de clics correspondant dansGameViewModel
. - Le score est mis à jour dans
GameViewModel
.
Les vues Button
et GameViewModel
ne communiquent pas directement. Elles ont besoin du listener de clic qui se trouve dans GameFragment
.
ViewModel transmis à la liaison de données
Il serait plus simple que les vues de la mise en page communiquent directement avec les données des objets ViewModel
, sans s'appuyer sur des contrôleurs d'UI comme intermédiaires.
Les objets ViewModel
contiennent toutes les données d'UI de l'application GuessTheWord. En transmettant des objets ViewModel
dans la liaison de données, vous pouvez automatiser une partie de la communication 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 avez également configuré des liaisons d'écouteur pour gérer les événements de clic.
Étape 1 : Ajoutez la liaison de données pour GameViewModel
Dans cette étape, vous associez GameViewModel
au fichier de mise en page correspondant, game_fragment.xml
.
- Dans le fichier
game_fragment.xml
, ajoutez une variable de liaison de données de typeGameViewModel
. Si vous rencontrez des erreurs dans Android Studio, nettoyez et recompilez le projet.
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- Dans le fichier
GameFragment
, transmettezGameViewModel
à la liaison de données.
Pour ce faire, attribuezviewModel
à la variablebinding.gameViewModel
que vous avez déclarée à l'étape précédente. Placez ce code dansonCreateView()
, après l'initialisation deviewModel
. Si vous rencontrez des erreurs dans Android Studio, nettoyez et recompilez 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 expressions "listener binding" pour la gestion des événements
Les expressions "listener binding" sont des expressions de liaison qui s'exécutent lorsque des événements tels que onClick()
, onZoomIn()
ou onZoomOut()
sont déclenchés. Les expressions "listener binding" sont écrites sous la forme d'expressions lambda.
La liaison de données crée un écouteur et le définit sur la vue. Lorsque l'événement écouté se produit, l'écouteur évalue l'expression lambda. Les expressions "listener binding" fonctionnent avec le plug-in Android Gradle version 2.0 ou ultérieure. Pour en savoir plus, consultez Mises en page et expressions de liaison.
Dans cette étape, vous allez remplacer les écouteurs de clics dans GameFragment
par des liaisons d'écouteur dans le fichier game_fragment.xml
.
- Dans
game_fragment.xml
, ajoutez l'attributonClick
àskip_button
. Définissez une expression de liaison et appelez la méthodeonSkip()
dansGameViewModel
. Cette expression de liaison est appelée listener binding.
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- De même, liez l'événement de clic de
correct_button
à la méthodeonCorrect
()
dansGameViewModel
.
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- Liez l'événement de clic de
end_game_button
à la méthodeonGameFinish
()
dansGameViewModel
.
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- 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 la liaison de données pour ScoreViewModel
Dans cette étape, vous associez ScoreViewModel
au fichier de mise en page correspondant, score_fragment.xml
.
- Dans le fichier
score_fragment.xml
, ajoutez une variable de liaison de typeScoreViewModel
. Cette étape est semblable à celle que vous avez suivie pourGameViewModel
ci-dessus.
<layout ...>
<data>
<variable
name="scoreViewModel"
type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
- Dans
score_fragment.xml
, ajoutez l'attributonClick
àplay_again_button
. Définissez une liaison d'écouteur et appelez la méthodeonPlayAgain()
dansScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- Dans
ScoreFragment
, à l'intérieur deonCreateView()
, initialisezviewModel
. Initialisez ensuite la variable de liaisonbinding.scoreViewModel
.
viewModel = ...
binding.scoreViewModel = viewModel
- Dans
ScoreFragment
, supprimez le code qui définit l'écouteur de clics pourplayAgainButton
. Si Android Studio affiche une erreur, nettoyez et recompilez le projet.
Code à supprimer :
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Exécutez votre application. Elle devrait fonctionner comme auparavant, mais les vues de bouton communiquent désormais directement avec les objets
ViewModel
. Les vues ne communiquent plus via les gestionnaires de clics sur les boutons dansScoreFragment
.
Résoudre les problèmes liés aux messages d'erreur de 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 comporter des erreurs qu'Android Studio ne détecte que lorsque vous essayez de la compiler. Vous ne voyez donc pas d'avertissements ni de code rouge lorsque vous écrivez le code. Toutefois, au moment de la compilation, vous obtenez des erreurs cryptiques provenant des classes intermédiaires générées.
Si vous recevez un message d'erreur obscur :
- Examinez attentivement le message dans le volet Compilation d'Android Studio. Si vous voyez un emplacement qui se termine par
databinding
, cela signifie qu'il y a une erreur de liaison de données. - Dans le fichier XML de mise en page, recherchez les erreurs dans les attributs
onClick
qui utilisent la liaison de données. Recherchez la fonction appelée par l'expression lambda et assurez-vous qu'elle existe. - Dans la section
<data>
du fichier XML, vérifiez l'orthographe de la variable de liaison de données.
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 la faute d'orthographe dans 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 alors 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
utilisé avec les objets ViewModel
. Maintenant que vous avez ajouté la liaison de données aux objets ViewModel
, vous êtes prêt à intégrer LiveData
.
Dans cette tâche, vous allez modifier l'application GuessTheWord pour qu'elle utilise 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
.
Étape 1 : Ajoutez LiveData au mot dans le fichier game_fragment.xml
Au cours de cette étape, vous allez lier l'affichage du mot actuel directement à l'objet LiveData
dans ViewModel
.
- Dans
game_fragment.xml
, ajoutez l'attributandroid:text
à l'affichage de texteword_text
.
Définissez-le sur l'objet LiveData
, word
à partir de GameViewModel
, à l'aide de 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 à la place. L'objet LiveData
affiche la valeur actuelle de word
. Si la valeur de word
est nulle, l'objet LiveData
affiche une chaîne vide.
- Dans
GameFragment
, dansonCreateView()
, après avoir initialiségameViewModel
, définissez l'activité actuelle comme propriétaire du cycle de vie de la variablebinding
. Cela définit le champ d'application de l'objetLiveData
ci-dessus, ce 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
- Dans
GameFragment
, supprimez l'observateur pourLiveData
word
.
Code à supprimer :
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
- Exécutez votre application et jouez. Le mot actuel est désormais mis à jour sans méthode d'observateur dans le contrôleur d'UI.
Étape 2 : Ajoutez score LiveData au fichier score_fragment.xml
Au cours de cette étape, vous allez lier le LiveData
score
à l'affichage du score dans le fragment de score.
- Dans
score_fragment.xml
, ajoutez l'attributandroid:text
à l'affichage de texte du score. AttribuezscoreViewModel.score
à l'attributtext
. Étant donné quescore
est un entier, convertissez-le en chaîne à l'aide deString.valueOf()
.
<TextView
android:id="@+id/score_text"
...
android:text="@{String.valueOf(scoreViewModel.score)}"
... />
- Dans
ScoreFragment
, après avoir initialiséscoreViewModel
, définissez l'activité actuelle comme propriétaire du cycle de vie de la variablebinding
.
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
- Dans
ScoreFragment
, supprimez l'observateur pour l'objetscore
.
Code à supprimer :
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Exécutez votre application et jouez. Notez que le score dans le fragment de score s'affiche correctement, sans observateur dans le fragment de score.
Étape 3 : Ajouter la mise en forme de chaîne avec la liaison de données
Dans la mise en page, vous pouvez ajouter la mise en forme de chaîne avec la liaison de données. Dans cette tâche, vous allez mettre en forme le mot actuel pour l'entourer de guillemets. Vous devez également mettre en forme la chaîne de score en y ajoutant le préfixe Current Score, comme illustré dans l'image suivante.
- Dans
string.xml
, ajoutez les chaînes suivantes, que vous utiliserez pour mettre en forme les affichages de texteword
etscore
.%s
et%d
sont les espaces réservés pour le mot et le score actuels.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- Dans
game_fragment.xml
, mettez à jour l'attributtext
de l'affichage de texteword_text
pour utiliser la ressource de chaînequote_format
. TransmettezgameViewModel.word
. Cela transmet le mot actuel en tant qu'argument à la chaîne de mise en forme.
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- Mettez en forme la vue de texte
score
de la même manière queword_text
. Dansgame_fragment.xml
, ajoutez l'attributtext
à l'affichage de textescore_text
. Utilisez la ressource de chaînescore_format
, qui accepte un argument numérique représenté par l'espace réservé%d
. Transmettez l'objetLiveData
,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)}"
... />
- Dans la classe
GameFragment
, à l'intérieur de la méthodeonCreateView()
, supprimez le code d'observateurscore
.
Code à supprimer :
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Nettoyez, recompilez et exécutez votre application, puis jouez. Notez que le mot actuel et le score sont mis en forme sur l'écran du jeu.
Félicitations ! Vous avez intégré LiveData
et ViewModel
à la liaison de données dans votre application. Cela permet aux vues de votre mise en page de communiquer directement avec ViewModel
, sans utiliser de gestionnaires de clics dans le fragment. Vous avez également utilisé des 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'observateur LiveData
.
Projet Android Studio : GuessTheWord
- La bibliothèque Data Binding fonctionne parfaitement avec les composants d'architecture Android tels que
ViewModel
etLiveData
. - Les mises en page de votre application peuvent être liées aux données des composants d'architecture, qui vous aident déjà à gérer le cycle de vie du contrôleur d'UI et à vous informer des modifications apportées aux données.
Liaison de données ViewModel
- Vous pouvez associer un
ViewModel
à une mise en page à l'aide de la liaison de données. - Les objets
ViewModel
contiennent les données d'UI. En transmettant des objetsViewModel
dans la liaison de données, vous pouvez automatiser une partie de la communication entre les vues et les objetsViewModel
.
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
, transmettezGameViewModel
à la liaison de données.
binding.gameViewModel = viewModel
Expressions "listener binding"
- Les liaisons d'écouteur sont des expressions de liaison dans la mise en page qui s'exécutent lorsque des événements de clic tels que
onClick()
sont déclenchés. - Les expressions "listener binding" sont écrites sous la forme d'expressions lambda.
- À l'aide des liaisons d'écouteur, vous remplacez les écouteurs de clics dans les contrôleurs d'UI par des liaisons d'écouteur dans le fichier de mise en page.
- La liaison de données crée un écouteur et le définit 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
dansViewModel
. Lorsque leLiveData
duViewModel
change, les vues de la mise en page peuvent être automatiquement mises à jour, sans les méthodes d'observation des contrôleurs d'UI.
android:text="@{gameViewModel.word}"
- Pour que la liaison de données
LiveData
fonctionne, définissez l'activité actuelle (le contrôleur d'UI) comme propriétaire du cycle de vie de la variablebinding
dans le contrôleur d'UI.
binding.lifecycleOwner = this
Mise en forme de chaînes avec la liaison de données
- Grâce à la liaison de données, vous pouvez mettre en forme une ressource de chaîne avec des espaces réservés tels que
%s
pour les chaînes et%d
pour les nombres entiers. - Pour mettre à jour l'attribut
text
de la vue, transmettez l'objetLiveData
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 suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :
- Attribuer des devoirs si nécessaire
- Indiquer aux élèves comment rendre leurs devoirs
- Noter les devoirs
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.
Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Répondre aux questions suivantes
Question 1
Parmi les affirmations suivantes concernant les expressions "listener binding", laquelle est fausse ?
- Les expressions "listener binding" sont des expressions de liaison qui s'exécutent lorsqu'un événement se produit.
- Les expressions "listener binding" fonctionnent avec toutes les versions du plug-in Android Gradle.
- Les expressions "listener binding" sont écrites sous la forme d'expressions lambda.
- Les expressions "listener binding" sont semblables aux références de méthodes, mais elles vous permettent d'exécuter des expressions de liaison de données arbitraires.
Question 2
Supposons que votre application comprenne cette ressource de chaîne :<string name="generic_name">Hello %s</string>
Parmi les syntaxes suivantes, laquelle permet de formater la chaîne à l'aide de l'expression de liaison de données ?
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
À quel moment une expression de type "listener-binding" est-elle évaluée et exécutée ?
- Lorsque les données stockées dans
LiveData
sont modifiées - Lorsqu'une activité est recréée suite à une modification de configuration
- Lorsqu'un événement tel que
onClick()
se produit - Lorsque l'activité passe en arrière-plan
Passez à la leçon suivante :
Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.