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
La plupart des applications qui utilisent des listes et des grilles pour afficher des éléments permettent aux utilisateurs d'interagir avec ces éléments. Appuyer sur un élément d'une liste et afficher ses détails est un cas d'utilisation très courant pour ce type d'interaction. Pour ce faire, vous pouvez ajouter des écouteurs de clic qui répondent aux appuis des utilisateurs sur les éléments en affichant une vue détaillée.
Dans cet atelier de programmation, vous allez ajouter de l'interaction à votre RecyclerView, en vous appuyant sur une version étendue de l'application de suivi du sommeil de la série d'ateliers de programmation précédente.
Ce que vous devez déjà savoir
- Créer une interface utilisateur de base à l'aide d'une activité, de fragments et de vues.
- Naviguer entre les fragments et utiliser
safeArgspour transmettre des données entre les fragments. - Affichez les modèles, les fabriques de modèles, les transformations et les
LiveData, ainsi que leurs observateurs. - Création d'une base de données
Room, d'un objet d'accès aux données (DAO) et définition d'entités - Utiliser des coroutines pour les tâches de base de données et autres tâches de longue durée
- Implémenter un
RecyclerViewde base avec une mise en pageAdapter,ViewHolderet d'élément. - Découvrez comment implémenter la liaison de données pour
RecyclerView. - Découvrez comment créer et utiliser des adaptateurs de liaison pour transformer des données.
- Comment utiliser
GridLayoutManager
Points abordés
- Comment rendre les éléments de
RecyclerViewcliquables. Implémentez un écouteur de clics pour accéder à une vue détaillée lorsqu'un élément est sélectionné.
Objectifs de l'atelier
- Développez une version étendue de l'application TrackMySleepQuality à partir de l'atelier de programmation précédent de cette série.
- Ajoutez un écouteur de clics à votre liste et commencez à écouter les interactions de l'utilisateur. Lorsqu'un utilisateur appuie sur un élément de la liste, il est redirigé vers un fragment contenant des informations sur l'élément sélectionné. Le code de démarrage fournit le code du fragment de détail, ainsi que le code de navigation.
L'application de suivi du sommeil de départ comporte deux écrans, représentés par des fragments, comme illustré dans la figure ci-dessous.
|
|
Le premier écran, affiché à gauche, comporte des boutons permettant de démarrer et d'arrêter le suivi. L'écran affiche certaines données de sommeil de l'utilisateur. Le bouton Effacer supprime définitivement toutes les données que l'application a collectées pour l'utilisateur. Le deuxième écran, à droite, permet de sélectionner une note de qualité du sommeil.
Cette application utilise une architecture simplifiée avec un contrôleur d'UI, un ViewModel et LiveData, ainsi qu'une base de données Room pour conserver les données de sommeil.

Dans cet atelier de programmation, vous allez ajouter la possibilité de répondre lorsqu'un utilisateur appuie sur un élément de la grille, ce qui affiche un écran de détails comme celui ci-dessous. Le code de cet écran (fragment, modèle de vue et navigation) est fourni avec l'application de démarrage. Vous implémenterez le mécanisme de gestion des clics.

Étape 1 : Télécharger l'application de démarrage
- Téléchargez le code de démarrage RecyclerViewClickHandler depuis GitHub et ouvrez le projet dans Android Studio.
- Créez et exécutez l'application de suivi du sommeil de départ.
[Facultatif] Mettez à jour votre application si vous souhaitez utiliser celle de l'atelier de programmation précédent.
Si vous prévoyez de travailler à partir de l'application de démarrage fournie dans GitHub pour cet atelier de programmation, passez à l'étape suivante.
Si vous souhaitez continuer à utiliser votre propre application de suivi du sommeil que vous avez créée dans l'atelier de programmation précédent, suivez les instructions ci-dessous pour mettre à jour votre application existante afin qu'elle contienne le code du fragment de l'écran de détails.
- Même si vous continuez à utiliser votre application existante, obtenez le code de démarrage RecyclerViewClickHandler sur GitHub pour pouvoir copier les fichiers.
- Copiez tous les fichiers du package
sleepdetail. - Dans le dossier
layout, copiez le fichierfragment_sleep_detail.xml. - Copiez le contenu mis à jour de
navigation.xml, qui ajoute la navigation poursleep_detail_fragment. - Dans le package
database, dansSleepDatabaseDao, ajoutez la nouvelle méthodegetNightWithId():
/**
* Selects and returns the night with given nightId.
*/
@Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
fun getNightWithId(key: Long): LiveData<SleepNight>- Dans
res/values/strings, ajoutez la ressource de chaîne suivante :
<string name="close">Close</string>- Nettoyez et recréez votre application pour mettre à jour la liaison de données.
Étape 2 : Inspecter le code de l'écran des détails du sommeil
Dans cet atelier de programmation, vous allez implémenter un gestionnaire de clics qui accède à un fragment affichant des informations sur la nuit de sommeil sur laquelle l'utilisateur a cliqué. Votre code de démarrage contient déjà le fragment et le graphique de navigation pour ce SleepDetailFragment, car il s'agit d'une quantité importante de code, et les fragments et la navigation ne font pas partie de cet atelier de programmation. Familiarisez-vous avec le code suivant :
- Dans votre application, recherchez le package
sleepdetail. Ce package contient le fragment, le ViewModel et la fabrique de ViewModel pour un fragment qui affiche les détails d'une nuit de sommeil. - Dans le package
sleepdetail, ouvrez et examinez le code deSleepDetailViewModel. Ce modèle de vue utilise la clé d'unSleepNightet un DAO dans le constructeur.
Le corps de la classe contient le code permettant d'obtenir leSleepNightpour la clé donnée et la variablenavigateToSleepTrackerpermettant de contrôler la navigation versSleepTrackerFragmentlorsque l'utilisateur appuie sur le bouton Fermer.
La fonctiongetNightWithId()renvoie unLiveData<SleepNight>et est définie dansSleepDatabaseDao(dans le packagedatabase). - Dans le package
sleepdetail, ouvrez et examinez le code deSleepDetailFragment. Notez la configuration de la liaison de données, du modèle de vue et de l'observateur pour la navigation. - Dans le package
sleepdetail, ouvrez et inspectez le code deSleepDetailViewModelFactory. - Dans le dossier de mise en page, inspectez
fragment_sleep_detail.xml. Notez la variablesleepDetailViewModeldéfinie dans la balise<data>pour obtenir les données à afficher dans chaque vue à partir du ViewModel.
La mise en page contient unConstraintLayoutqui contient unImageViewpour la qualité du sommeil, unTextViewpour une note de qualité, unTextViewpour la durée du sommeil et unButtonpour fermer le fragment de détails. - Ouvrez le fichier
navigation.xml. Poursleep_tracker_fragment, notez la nouvelle action poursleep_detail_fragment.
La nouvelle action,action_sleep_tracker_fragment_to_sleepDetailFragment, correspond à la navigation du fragment du suivi du sommeil vers l'écran de détails.
Dans cette tâche, vous allez mettre à jour RecyclerView pour répondre aux appuis de l'utilisateur en affichant un écran de détails pour l'élément sélectionné.
La réception et la gestion des clics sont une tâche en deux parties : vous devez d'abord écouter et recevoir le clic, puis déterminer sur quel élément il a été effectué. Vous devez ensuite répondre au clic par une action.
Quel est le meilleur endroit pour ajouter un écouteur de clics pour cette application ?
- Le
SleepTrackerFragmenthéberge de nombreuses vues. Par conséquent, l'écoute des événements de clic au niveau du fragment ne vous indiquera pas sur quel élément l'utilisateur a cliqué. Il ne vous indiquera même pas s'il s'agissait d'un élément sur lequel l'utilisateur a cliqué ou d'un autre élément d'interface utilisateur. - Si vous écoutez au niveau
RecyclerView, il est difficile de déterminer exactement sur quel élément de la liste l'utilisateur a cliqué. - Le meilleur rythme pour obtenir des informations sur un élément cliqué se trouve dans l'objet
ViewHolder, car il représente un élément de liste.
Bien que le ViewHolder soit idéal pour écouter les clics, il n'est généralement pas adapté pour les gérer. Alors, quel est le meilleur endroit pour gérer les clics ?
- L'
Adapteraffiche des éléments de données dans des vues afin que vous puissiez gérer les clics. Sa tâche consiste à adapter les données à afficher, pas à traiter la logique de l'application. - En général, les clics doivent être gérés dans le
ViewModel, car il a accès aux données et à la logique permettant de déterminer comment réagir au clic.ViewModel
Étape 1 : Créez un écouteur de clics et déclenchez-le à partir de la mise en page de l'élément
- Dans le dossier
sleeptracker, ouvrez SleepNightAdapter.kt. - À la fin du fichier, au niveau supérieur, créez une classe d'écouteur,
SleepNightListener.
class SleepNightListener() {
}- Dans la classe
SleepNightListener, ajoutez une fonctiononClick(). Lorsque l'utilisateur clique sur la vue qui affiche un élément de liste, la vue appelle cette fonctiononClick(). (Vous définirez ultérieurement la propriétéandroid:onClickde la vue sur cette fonction.)
class SleepNightListener() {
fun onClick() =
}- Ajoutez un argument de fonction
nightde typeSleepNightàonClick(). La vue sait quel élément elle affiche, et cette information doit être transmise pour gérer le clic.
class SleepNightListener() {
fun onClick(night: SleepNight) =
}- Pour définir ce que fait
onClick(), fournissez un rappelclickListenerdans le constructeur deSleepNightListeneret attribuez-le àonClick().
Donner un nom au lambda qui gère le clic,clickListener, permet de le suivre lorsqu'il est transmis entre les classes. Le rappelclickListenern'a besoin que denight.nightIdpour accéder aux données de la base de données. Votre classeSleepNightListenerterminée devrait ressembler au code ci-dessous.
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
fun onClick(night: SleepNight) = clickListener(night.nightId)
}- Ouvrez list_item_sleep_night.xml..
- Dans le bloc
data, ajoutez une variable pour rendre la classeSleepNightListenerdisponible via la liaison de données. Attribuez à la nouvelle<variable>unnamedeclickListener.. Définisseztypesur le nom complet de la classecom.example.android.trackmysleepquality.sleeptracker.SleepNightListener, comme indiqué ci-dessous. Vous pouvez désormais accéder à la fonctiononClick()dansSleepNightListenerà partir de cette mise en page.
<variable
name="clickListener"
type="com.example.android.trackmysleepquality.sleeptracker.SleepNightListener" />- Pour écouter les clics sur n'importe quelle partie de cet élément de liste, ajoutez l'attribut
android:onClickàConstraintLayout.
Définissez l'attribut surclickListener:onClick(sleep)à l'aide d'un lambda de liaison de données, comme indiqué ci-dessous :
android:onClick="@{() -> clickListener.onClick(sleep)}"Étape 2 : Transmettez l'écouteur de clic au support de vue et à l'objet de liaison
- Ouvrez SleepNightAdapter.kt.
- Modifiez le constructeur de la classe
SleepNightAdapterpour recevoir unval clickListener: SleepNightListener. Lorsque l'adaptateur lieViewHolder, il doit lui fournir cet écouteur de clics.
class SleepNightAdapter(val clickListener: SleepNightListener):
ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {- Dans
onBindViewHolder(), mettez à jour l'appel àholder.bind()pour transmettre également l'écouteur de clics àViewHolder. Vous obtiendrez une erreur de compilation, car vous avez ajouté un paramètre à l'appel de fonction.
holder.bind(getItem(position)!!, clickListener)- Ajoutez le paramètre
clickListeneràbind(). Pour ce faire, placez le curseur sur l'erreur, puis appuyez surAlt+Enter(Windows) ouOption+Enter(Mac) pour la corriger , comme indiqué dans la capture d'écran ci-dessous.
- Dans la classe
ViewHolder, dans la fonctionbind(), attribuez l'écouteur de clics à l'objetbinding. Une erreur s'affiche, car vous devez mettre à jour l'objet de liaison.
binding.clickListener = clickListener- Pour mettre à jour la liaison de données, nettoyez et recompilez votre projet. (Vous devrez peut-être également invalider les caches.) Vous avez donc récupéré un écouteur de clics à partir du constructeur de l'adaptateur et l'avez transmis jusqu'au support de vue et à l'objet de liaison.
Étape 3 : Afficher un toast lorsqu'un élément est sélectionné
Vous avez maintenant le code en place pour capturer un clic, mais vous n'avez pas implémenté ce qui se passe lorsqu'un élément de liste est sélectionné. La réponse la plus simple consiste à afficher un toast indiquant nightId lorsqu'un élément est sélectionné. Cela permet de vérifier que, lorsqu'un élément de la liste est sélectionné, le nightId approprié est capturé et transmis.
- Ouvrez SleepTrackerFragment.kt.
- Dans
onCreateView(), recherchez la variableadapter. Notez qu'une erreur s'affiche, car il attend désormais un paramètre d'écouteur de clic. - Définissez un écouteur de clics en transmettant un lambda à
SleepNightAdapter. Ce lambda simple affiche simplement un toast indiquant lenightId, comme indiqué ci-dessous. Vous devrez importerToast. Vous trouverez ci-dessous la définition complète mise à jour.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})- Exécutez l'application, appuyez sur des éléments et vérifiez qu'ils affichent un toast avec le
nightIdapproprié. Étant donné que les éléments ont des valeursnightIdcroissantes et que l'application affiche la nuit la plus récente en premier, l'élément avec la valeurnightIdla plus basse se trouve en bas de la liste.
Dans cette tâche, vous allez modifier le comportement lorsqu'un élément du RecyclerView est sélectionné. Au lieu d'afficher un toast, l'application accède à un fragment de détails qui affiche plus d'informations sur la nuit sélectionnée.
Étape 1 : Naviguer au clic
Au cours de cette étape, au lieu d'afficher un toast, vous allez modifier le lambda de l'écouteur de clics dans onCreateView() de SleepTrackerFragment pour transmettre nightId à SleepTrackerViewModel et déclencher la navigation vers SleepDetailFragment.
Définissez la fonction de gestionnaire de clics :
- Ouvrez SleepTrackerViewModel.kt.
- Dans
SleepTrackerViewModel, vers la fin, définissez la fonction de gestionnaire de clicsonSleepNightClicked().
fun onSleepNightClicked(id: Long) {
}- Dans
onSleepNightClicked(), déclenchez la navigation en définissant_navigateToSleepDetailsur leidtransmis de la nuit de sommeil sur laquelle l'utilisateur a cliqué.
fun onSleepNightClicked(id: Long) {
_navigateToSleepDetail.value = id
}- Mettre en œuvre
_navigateToSleepDetailComme vous l'avez fait précédemment, définissez unprivate MutableLiveDatapour l'état de navigation. Et un getter publicvalpour l'accompagner.
private val _navigateToSleepDetail = MutableLiveData<Long>()
val navigateToSleepDetail
get() = _navigateToSleepDetail- Définissez la méthode à appeler une fois la navigation de l'application terminée. Appelez-la
onSleepDetailNavigated()et définissez sa valeur surnull.
fun onSleepDetailNavigated() {
_navigateToSleepDetail.value = null
}Ajoutez le code pour appeler le gestionnaire de clics :
- Ouvrez SleepTrackerFragment.kt et faites défiler la page jusqu'au code qui crée l'adaptateur et définit
SleepNightListenerpour afficher un toast.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})- Ajoutez le code suivant sous le toast pour appeler un gestionnaire de clics,
onSleepNighClicked(), danssleepTrackerViewModellorsqu'un élément est sélectionné. Transmettez l'nightIdpour que le ViewModel sache quelle nuit de sommeil récupérer. Un message d'erreur s'affiche, car vous n'avez pas encore définionSleepNightClicked(). Vous pouvez conserver, commenter ou supprimer le toast, selon vos préférences.
sleepTrackerViewModel.onSleepNightClicked(nightId)Ajoutez le code pour observer les clics :
- Ouvrez SleepTrackerFragment.kt.
- Dans
onCreateView(), juste au-dessus de la déclaration demanager, ajoutez du code pour observer le nouveauLiveDatanavigateToSleepDetail. LorsquenavigateToSleepDetailchange, accédez àSleepDetailFragmenten transmettantnight, puis appelezonSleepDetailNavigated(). Comme vous l 'avez déjà fait dans un atelier de programmation précédent, voici le code :
sleepTrackerViewModel.navigateToSleepDetail.observe(this, Observer { night ->
night?.let {
this.findNavController().navigate(
SleepTrackerFragmentDirections
.actionSleepTrackerFragmentToSleepDetailFragment(night))
sleepTrackerViewModel.onSleepDetailNavigated()
}
})- Exécutez votre code, cliquez sur un élément et… l'application plante.
Gérez les valeurs nulles dans les adaptateurs de liaison :
- Exécutez à nouveau l'application en mode débogage. Appuyez sur un élément, puis filtrez les journaux pour afficher les erreurs. Une trace de pile s'affichera, avec un message semblable à celui ci-dessous.
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter itemMalheureusement, la trace de la pile n'indique pas clairement où cette erreur est déclenchée. L'un des inconvénients de la liaison de données est qu'elle peut rendre le débogage de votre code plus difficile. L'application plante lorsque vous cliquez sur un élément, et le seul nouveau code sert à gérer le clic.
Toutefois, il s'avère qu'avec ce nouveau mécanisme de gestion des clics, il est désormais possible que les adaptateurs de liaison soient appelés avec une valeur null pour item. En particulier, lorsque l'application démarre, LiveData commence par null. Vous devez donc ajouter des vérifications de valeur nulle à chacun des adaptateurs.
- Dans
BindingUtils.kt, pour chacun des adaptateurs de liaison, remplacez le type de l'argumentitempar "nullable" et encapsulez le corps avecitem?.let{...}. Par exemple, votre adaptateur poursleepQualityStringse présentera comme suit. Modifiez les autres adaptateurs de la même manière.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight?) {
item?.let {
text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
}- Exécutez votre application. Appuyez sur un élément pour ouvrir une vue détaillée.
Projet Android Studio : RecyclerViewClickHandler.
Pour que les éléments d'un RecyclerView réagissent aux clics, associez des écouteurs de clics aux éléments de liste dans le ViewHolder et gérez les clics dans le ViewModel.
Pour que les éléments d'un RecyclerView réagissent à la suite d'un clic, vous devez procéder comme suit :
- Créez une classe d'écouteur qui prend un lambda et l'attribue à une fonction
onClick().
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
fun onClick(night: SleepNight) = clickListener(night.nightId)
}- Définissez l'écouteur de clics sur la vue.
android:onClick="@{() -> clickListener.onClick(sleep)}"- Transmettez l'écouteur de clics au constructeur de l'adaptateur, dans le support de vue, et ajoutez-le à l'objet de liaison.
class SleepNightAdapter(val clickListener: SleepNightListener):
ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()holder.bind(getItem(position)!!, clickListener)binding.clickListener = clickListener- Dans le fragment qui affiche la vue Recycler, où vous créez l'adaptateur, définissez un écouteur de clic en transmettant un lambda à l'adaptateur.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
sleepTrackerViewModel.onSleepNightClicked(nightId)
})- Implémentez le gestionnaire de clics dans le ViewModel. Pour les clics sur les éléments de la liste, cela déclenche généralement la navigation vers un fragment de détails.
Cours Udacity :
- Développer des applications Android avec Kotlin
- Kotlin Bootcamp for Programmers (Formation Kotlin pour les programmeurs)
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
Supposons que votre application contienne un RecyclerView qui affiche les éléments d'une liste de courses. Votre application définit également une classe d'écouteur de clics :
class ShoppingListItemListener(val clickListener: (itemId: Long) -> Unit) {
fun onClick(cartItem: CartItem) = clickListener(cartItem.itemId)
}Comment rendre ShoppingListItemListener disponible pour la liaison de données ? Sélectionnez une option.
▢ Dans le fichier de mise en page contenant le RecyclerView qui affiche la liste de courses, ajoutez une variable <data> pour ShoppingListItemListener.
▢ Dans le fichier de mise en page qui définit la mise en page d'une seule ligne de la liste de courses, ajoutez une variable <data> pour ShoppingListItemListener.
▢ Dans la classe ShoppingListItemListener, ajoutez une fonction pour activer la liaison de données :
fun onBinding (cartItem: CartItem) {dataBindingEnable(true)}▢ Dans la classe ShoppingListItemListener, dans la fonction onClick(), ajoutez un appel pour activer la liaison de données :
fun onClick(cartItem: CartItem) = {
clickListener(cartItem.itemId)
dataBindingEnable(true)
}Question 2
Où faut-il ajouter l'attribut android:onClick pour que les éléments d'un RecyclerView réagissent à la suite d'un clic ? Plusieurs réponses possibles.
▢ Ajoutez-le à <androidx.recyclerview.widget.RecyclerView> dans le fichier de mise en page affichant le RecyclerView.
▢ Ajoutez-le au fichier de mise en page d'un élément de la ligne. Si vous souhaitez que l'ensemble de l'élément soit cliquable, ajoutez-le à la vue parent contenant les éléments de la ligne.
▢ Ajoutez-le au fichier de mise en page d'un élément de la ligne. Si vous souhaitez qu'un seul TextView de l'élément soit cliquable, ajoutez-le à <TextView>.
▢ Ajoutez-le toujours au fichier de mise en page pour MainActivity.
Passez à la leçon suivante :

