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
Cet atelier de programmation vous explique comment utiliser un RecyclerView pour afficher des listes d'éléments. En vous appuyant sur l'application de suivi du sommeil de la série d'ateliers de programmation précédente, vous allez découvrir une façon plus efficace et polyvalente d'afficher les données à l'aide d'un RecyclerView avec une architecture recommandée.
Ce que vous devez déjà savoir
Vous devez maîtriser les éléments suivants :
- Créer une interface utilisateur (UI) 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. - Utilisation des ViewModels, des ViewModelFactories, des transformations, de
LiveDataet de leurs observateurs. - Créer une base de données
Room, un DAO et définir des entités - Utiliser des coroutines pour les tâches de base de données et autres tâches de longue durée.
Points abordés
- Comment utiliser un
RecyclerViewavec unAdapteret unViewHolderpour afficher une liste d'éléments.
Objectifs de l'atelier
- Modifiez l'application TrackMySleepQuality de la leçon précédente pour qu'elle utilise un
RecyclerViewafin d'afficher les données sur la qualité du sommeil.
Dans cet atelier de programmation, vous allez créer la partie RecyclerView d'une application qui suit la qualité du sommeil. L'application utilise une base de données Room pour stocker les données de sommeil au fil du temps.
L'application de suivi du sommeil de démarrage 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. Cet écran affiche également toutes les 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, ViewModel et LiveData. L'application utilise également une base de données Room pour rendre les données de sommeil persistantes.

La liste des nuits de sommeil affichée sur le premier écran est fonctionnelle, mais pas très esthétique. L'application utilise un formateur complexe pour créer des chaînes de texte pour la vue de texte et des nombres pour la qualité. De plus, cette conception n'est pas évolutive. Une fois que vous aurez résolu tous ces problèmes dans cet atelier de programmation, l'application finale aura les mêmes fonctionnalités et l'écran principal ressemblera à ceci :

L'affichage d'une liste ou d'une grille de données est l'une des tâches d'UI les plus courantes dans Android. Les listes peuvent être simples ou très complexes. Une liste de vues de texte peut afficher des données simples, comme une liste de courses. Une liste complexe, comme une liste annotée de destinations de vacances, peut afficher de nombreux détails à l'utilisateur dans une grille à faire défiler avec des en-têtes.
Pour prendre en charge tous ces cas d'utilisation, Android fournit le widget RecyclerView.

Le principal avantage de RecyclerView est son efficacité pour les listes volumineuses :
- Par défaut,
RecyclerViewpermet seulement de traiter ou dessiner les éléments actuellement visibles à l'écran. Par exemple, si votre liste comporte 1 000 éléments, mais que seuls 10 éléments sont visibles,RecyclerViewse contente de dessiner 10 éléments à l'écran. Lorsque l'utilisateur fait défiler la page,RecyclerViewidentifie les nouveaux éléments à l'écran et affiche juste ces éléments. - Lorsqu'un élément n'est plus affiché à l'écran, ses vues sont recyclées. Cela signifie que l'élément affiche le nouveau contenu qui défile à l'écran. Ce comportement
RecyclerViewvous fait gagner beaucoup de temps de traitement et garantit un défilement fluide des listes. - Lorsqu'un élément change, au lieu de redessiner toute la liste,
RecyclerViewpeut mettre à jour cet élément. Cela représente un gain d'efficacité considérable lors de l'affichage de listes d'éléments complexes.
Dans la séquence ci-dessous, vous pouvez voir que les données ABC ont été affichées dans une vue. Une fois que cette vue est sortie de l'écran, RecyclerView la réutilise pour les nouvelles données, XYZ.
Modèle d'adaptateur
Si vous voyagez entre des pays qui utilisent des prises électriques différentes, vous savez probablement comment brancher vos appareils à l'aide d'un adaptateur. L'adaptateur vous permet de convertir un type de prise en un autre, ce qui revient à convertir une interface en une autre.
Le modèle d'adaptateur en ingénierie logicielle aide un objet à fonctionner avec une autre API. RecyclerView utilise un adaptateur pour transformer les données de l'application en un format que RecyclerView peut afficher, sans modifier la façon dont l'application stocke et traite les données. Pour l'application de suivi du sommeil, vous créez un adaptateur qui adapte les données de la base de données Room en un format que RecyclerView sait afficher, sans modifier ViewModel.
Implémenter un RecyclerView

Pour afficher vos données dans un RecyclerView, vous avez besoin des éléments suivants :
- Données à afficher.
- Une instance
RecyclerViewdéfinie dans votre fichier de mise en page, qui sert de conteneur pour les vues. - Mise en page pour un élément de données.
Si tous les éléments de la liste se ressemblent, vous pouvez utiliser la même mise en page pour tous, mais ce n'est pas obligatoire. La mise en page de l'élément doit être créée séparément de celle du fragment, afin qu'une vue d'élément puisse être créée et remplie avec des données à la fois. - Gestionnaire de mise en page.
Le gestionnaire de mise en page gère l'organisation (la mise en page) des composants d'interface utilisateur dans une vue. - Support de vue.
Le support de vue étend la classeViewHolder. Il contient les informations de la vue permettant d'afficher un élément de la mise en page de l'élément. Les conteneurs de vues ajoutent également des informations queRecyclerViewutilise pour déplacer efficacement des vues sur l'écran. - Un adaptateur.
L'adaptateur connecte vos données àRecyclerView. Il adapte les données pour qu'elles puissent être affichées dans unViewHolder. UnRecyclerViewutilise l'adaptateur pour déterminer comment afficher les données à l'écran.
Dans cette tâche, vous allez ajouter un RecyclerView à votre fichier de mise en page et configurer un Adapter pour exposer les données de sommeil au RecyclerView.
Étape 1 : Ajouter RecyclerView avec LayoutManager
Dans cette étape, vous allez remplacer le ScrollView par un RecyclerView dans le fichier fragment_sleep_tracker.xml.
- Téléchargez l'application RecyclerViewFundamentals-Starter depuis GitHub.
- Créez et exécutez l'application. Notez que les données sont affichées sous forme de texte simple.
- Ouvrez le fichier de mise en page
fragment_sleep_tracker.xmldans l'onglet Design d'Android Studio. - Dans le volet Arborescence des composants, supprimez
ScrollView. Cette action supprime également leTextViewqui se trouve dans leScrollView. - Dans le volet Palette, faites défiler la liste des types de composants sur la gauche pour trouver Conteneurs, puis sélectionnez-le.
- Faites glisser un
RecyclerViewdu volet Palette vers le volet Arborescence des composants. Placez leRecyclerViewdans leConstraintLayout.

- Si une boîte de dialogue s'ouvre pour vous demander si vous souhaitez ajouter une dépendance, cliquez sur OK pour permettre à Android Studio d'ajouter la dépendance
recyclerviewà votre fichier Gradle. La synchronisation de votre application peut prendre quelques secondes.

- Ouvrez le fichier
build.gradledu module, faites défiler la page jusqu'à la fin et notez la nouvelle dépendance, qui ressemble au code ci-dessous :
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- Rebasculez sur
fragment_sleep_tracker.xml. - Dans l'onglet Texte, recherchez le code
RecyclerViewindiqué ci-dessous :
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />- Attribuez l'
iddesleep_listàRecyclerView.
android:id="@+id/sleep_list"- Positionnez le
RecyclerViewpour qu'il occupe la partie restante de l'écran à l'intérieur duConstraintLayout. Pour ce faire, limitez le haut deRecyclerViewau bouton Start (Démarrer), le bas au bouton Clear (Effacer) et chaque côté au parent. Définissez la largeur et la hauteur de la mise en page sur 0 dp dans l'éditeur de mise en page ou dans le fichier XML, à l'aide du code suivant :
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"- Ajoutez un gestionnaire de mise en page au fichier XML
RecyclerView. ChaqueRecyclerViewa besoin d'un gestionnaire de mise en page qui lui indique comment positionner les éléments dans la liste. Android fournit unLinearLayoutManager, qui dispose par défaut les éléments dans une liste verticale de lignes de pleine largeur.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"- Passez à l'onglet Conception et remarquez que les contraintes ajoutées ont entraîné l'expansion de
RecyclerViewpour remplir l'espace disponible.

Étape 2 : Créez la mise en page de l'élément de liste et le support de vue de texte
RecyclerView n'est qu'un conteneur. Dans cette étape, vous allez créer la mise en page et l'infrastructure pour les éléments à afficher dans RecyclerView.
Pour obtenir un RecyclerView fonctionnel le plus rapidement possible, vous commencez par utiliser un élément de liste simpliste qui n'affiche la qualité du sommeil que sous forme de nombre. Pour cela, vous avez besoin d'un support de vue, TextItemViewHolder. Vous avez également besoin d'une vue, d'un TextView, pour les données. (Dans une étape ultérieure, vous en apprendrez plus sur les porte-vues et sur la façon de présenter toutes les données de sommeil.)
- Créez un fichier de mise en page appelé
text_item_view.xml. L'élément racine que vous utilisez n'a pas d'importance, car vous allez remplacer le code du modèle. - Dans
text_item_view.xml, supprimez tout le code fourni. - Ajoutez un
TextViewavec une marge intérieure de16dpau début et à la fin, et une taille de texte de24sp. La largeur doit correspondre à celle du parent et la hauteur doit s'adapter au contenu. Étant donné que cette vue est affichée dansRecyclerView, vous n'avez pas besoin de la placer dans unViewGroup.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />- Ouvrez
Util.kt. Faites défiler la page jusqu'à la fin et ajoutez la définition ci-dessous, qui crée la classeTextItemViewHolder. Placez le code en bas du fichier, après la dernière accolade fermante. Le code est placé dansUtil.kt, car ce support de vue est temporaire et vous le remplacerez ultérieurement.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)- Si vous y êtes invité, importez
android.widget.TextViewetandroidx.recyclerview.widget.RecyclerView.
Étape 3 : Créer SleepNightAdapter
La tâche principale lors de l'implémentation d'un RecyclerView consiste à créer l'adaptateur. Vous disposez d'un simple conteneur de vue pour la vue de l'élément et d'une mise en page pour chaque élément. Vous pouvez maintenant créer un adaptateur. L'adaptateur crée un support de vue et le remplit de données pour que RecyclerView les affiche.
- Dans le package
sleeptracker, créez une classe Kotlin appeléeSleepNightAdapter. - Faites en sorte que la classe
SleepNightAdapterétendeRecyclerView.Adapter. La classe est appeléeSleepNightAdapter, car elle adapte un objetSleepNighten quelque chose queRecyclerViewpeut utiliser. L'adaptateur doit savoir quel support de vue utiliser. Transmettez doncTextItemViewHolder. Importez les composants nécessaires lorsque vous y êtes invité, puis une erreur s'affiche, car il existe des méthodes obligatoires à implémenter.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}- Au niveau supérieur de
SleepNightAdapter, créez une variablelistOfSleepNightpour contenir les données.
var data = listOf<SleepNight>()- Dans
SleepNightAdapter, remplacezgetItemCount()pour renvoyer la taille de la liste des nuits de sommeil dansdata.RecyclerViewdoit savoir combien d'éléments l'adaptateur doit afficher. Pour ce faire, il appellegetItemCount().
override fun getItemCount() = data.size- Dans
SleepNightAdapter, remplacez la fonctiononBindViewHolder(), comme indiqué ci-dessous.
La fonctiononBindViewHolder()est appelée parRecyclerViewpour afficher les données d'un élément de liste à la position spécifiée. La méthodeonBindViewHolder()prend donc deux arguments : un support de vue et une position des données à lier. Pour cette application, le support estTextItemViewHolderet la position est la position dans la liste.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}- Dans
onBindViewHolder(), créez une variable pour un élément à une position donnée dans les données.
val item = data[position]- Le
ViewHolderque vous avez créé possède une propriété appeléetextView. DansonBindViewHolder(), définissez letextdetextViewsur le nombre correspondant à la qualité du sommeil. Ce code n'affiche qu'une liste de nombres, mais cet exemple simple vous permet de voir comment l'adaptateur insère les données dans le support de vue et à l'écran.
holder.textView.text = item.sleepQuality.toString()- Dans
SleepNightAdapter, remplacez et implémentezonCreateViewHolder(), qui est appelé lorsqueRecyclerViewa besoin d'un conteneur de vue pour représenter un élément.
Cette fonction accepte deux paramètres et renvoie unViewHolder. Le paramètreparent, qui est le groupe de vues contenant le support de vue, est toujoursRecyclerView. Le paramètreviewTypeest utilisé lorsqu'une mêmeRecyclerViewcontient plusieurs vues. Par exemple, si vous placez une liste de vues de texte, une image et une vidéo dans le mêmeRecyclerView, la fonctiononCreateViewHolder()doit savoir quel type de vue utiliser.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}- Dans
onCreateViewHolder(), créez une instance deLayoutInflater.
Le système de gonflage de mise en page sait comment créer des vues à partir de mises en page XML. Lecontextcontient des informations sur la façon d'augmenter correctement la vue. Dans un adaptateur pour une vue du recycleur, vous transmettez toujours le contexte du groupe de vuesparent, qui estRecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)- Dans
onCreateViewHolder(), créez leviewen demandant àlayoutinflaterde le développer.
Transmettez la mise en page XML de la vue et le groupe de vuesparentde la vue. Le troisième argument booléen estattachToRoot. Cet argument doit êtrefalse, carRecyclerViewajoute automatiquement cet élément à la hiérarchie des vues le moment venu.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView- Dans
onCreateViewHolder(), renvoyez unTextItemViewHoldercréé avecview.
return TextItemViewHolder(view)- L'adaptateur doit informer
RecyclerViewlorsquedataa changé, carRecyclerViewne connaît rien des données. Il ne connaît que les holders de vue que l'adaptateur lui fournit.
Pour indiquer àRecyclerViewquand les données qu'il affiche ont changé, ajoutez un setter personnalisé à la variabledataqui se trouve en haut de la classeSleepNightAdapter. Dans le setter, attribuez une nouvelle valeur àdata, puis appeleznotifyDataSetChanged()pour déclencher le redessin de la liste avec les nouvelles données.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}Étape 4 : Informez RecyclerView de l'adaptateur
RecyclerView doit connaître l'adaptateur à utiliser pour obtenir les supports de vue.
- Ouvrez
SleepTrackerFragment.kt. - Dans
onCreateview(), créez un adaptateur. Placez ce code après la création du modèleViewModelet avant l'instructionreturn.
val adapter = SleepNightAdapter()- Associez
adapteràRecyclerView.
binding.sleepList.adapter = adapter- Nettoyez et recréez votre projet pour mettre à jour l'objet
binding.
Si des erreurs concernantbinding.sleepListoubinding.FragmentSleepTrackerBindings'affichent toujours, invalidez les caches et redémarrez. (Sélectionnez File > Invalidate Caches / Restart.)
Si vous exécutez l'application maintenant, aucune erreur ne s'affiche, mais aucune donnée n'est affichée lorsque vous appuyez sur Start (Démarrer), puis sur Stop (Arrêter).
Étape 5 : Transférez les données dans l'adaptateur
Jusqu'à présent, vous avez un adaptateur et un moyen d'obtenir des données de l'adaptateur dans le RecyclerView. Vous devez maintenant insérer des données dans l'adaptateur à partir de ViewModel.
- Ouvrez
SleepTrackerViewModel. - Recherchez la variable
nights, qui stocke toutes les nuits de sommeil, c'est-à-dire les données à afficher. La variablenightsest définie en appelantgetAllNights()sur la base de données. - Supprimez
privatedenights, car vous allez créer un observateur qui doit accéder à cette variable. Votre déclaration doit se présenter comme suit :
val nights = database.getAllNights()- Dans le package
database, ouvrezSleepDatabaseDao. - Recherchez la fonction
getAllNights(). Notez que cette fonction renvoie une liste de valeursSleepNightsous la formeLiveData. Cela signifie que la variablenightscontientLiveData, qui est tenu à jour parRoom. Vous pouvez observernightspour savoir quand il change. - Ouvrez
SleepTrackerFragment. - Dans
onCreateView(), sous la création deadapter, créez un observateur sur la variablenights.
En fournissant leviewLifecycleOwnerdu fragment comme propriétaire du cycle de vie, vous pouvez vous assurer que cet observateur n'est actif que lorsqueRecyclerViewest à l'écran.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})- Dans l'observateur, chaque fois que vous obtenez une valeur non nulle (pour
nights), attribuez la valeur àdatade l'adaptateur. Voici le code finalisé pour l'observateur et la définition des données :
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})- Compilez et exécutez votre code.
Si votre adaptateur fonctionne, les chiffres de qualité du sommeil s'affichent sous forme de liste. La capture d'écran de gauche montre "-1" après que vous avez appuyé sur Démarrer. La capture d'écran de droite montre le nombre de qualité du sommeil mis à jour après que vous avez appuyé sur Arrêter et sélectionné une note de qualité.

Étape 6 : Découvrez comment les porte-vues sont recyclés
RecyclerView recycle les porte-vues, ce qui signifie qu'il les réutilise. Lorsqu'une vue défile hors de l'écran, RecyclerView réutilise cette vue pour celle qui est sur le point de défiler à l'écran.
Étant donné que ces porte-vues sont recyclés, assurez-vous que onBindViewHolder() définit ou réinitialise toutes les personnalisations que les éléments précédents ont pu définir sur un porte-vue.
Par exemple, vous pouvez définir la couleur du texte sur rouge dans les espaces réservés pour les vues qui contiennent des notes de qualité inférieures ou égales à 1 et qui représentent un mauvais sommeil.
- Dans la classe
SleepNightAdapter, ajoutez le code suivant à la fin deonBindViewHolder().
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}- Exécutez l'application.
- Si vous ajoutez des données de mauvaise qualité sur le sommeil, le nombre devient rouge.
- Ajoutez des notes élevées pour la qualité du sommeil jusqu'à ce qu'un nombre rouge élevé s'affiche à l'écran.
CommeRecyclerViewréutilise les emplacements de vue, il finit par réutiliser l'un des emplacements de vue rouges pour une note de qualité élevée. La note élevée s'affiche à tort en rouge.

- Pour résoudre ce problème, ajoutez une instruction
elseafin de définir la couleur sur noir si la qualité n'est pas inférieure ou égale à un.
Avec les deux conditions explicites, le support de vue utilisera la couleur de texte appropriée pour chaque élément.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}- Exécutez l'application. Les nombres doivent toujours avoir la bonne couleur.
Félicitations ! Vous disposez désormais d'une RecyclerView de base entièrement fonctionnelle.
Dans cette tâche, vous allez remplacer le simple support de vue par un support capable d'afficher plus de données pour une nuit de sommeil.
Le ViewHolder simple que vous avez ajouté à Util.kt encapsule simplement un TextView dans un TextItemViewHolder.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)Alors pourquoi RecyclerView n'utilise-t-il pas directement un TextView ? Cette ligne de code unique offre de nombreuses fonctionnalités. Un ViewHolder décrit une vue d'élément et les métadonnées concernant sa place dans le RecyclerView. RecyclerView s'appuie sur cette fonctionnalité pour positionner correctement la vue lorsque la liste défile, et pour effectuer des actions intéressantes comme animer les vues lorsque des éléments sont ajoutés ou supprimés dans Adapter.
Si RecyclerView doit accéder aux vues stockées dans ViewHolder, il peut le faire à l'aide de la propriété itemView du support de vue. RecyclerView utilise itemView lorsqu'il lie un élément à afficher à l'écran, lorsqu'il dessine des décorations autour d'une vue, comme une bordure, et pour implémenter l'accessibilité.
Étape 1 : Créez la mise en page de l'article
Dans cette étape, vous allez créer le fichier de mise en page pour un élément. La mise en page se compose d'un ConstraintLayout avec un ImageView pour la qualité du sommeil, un TextView pour la durée du sommeil et un TextView pour la qualité sous forme de texte. Comme vous avez déjà créé des mises en page, copiez et collez le code XML fourni.
- Créez un fichier de ressources de mise en page et nommez-le
list_item_sleep_night. - Remplacez tout le code du fichier par le code ci-dessous. Familiarisez-vous ensuite avec la mise en page que vous venez de créer.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>- Passez à l'onglet Design dans Android Studio. En mode Conception, votre mise en page ressemble à la capture d'écran de gauche ci-dessous. Dans la vue Schéma, il ressemble à la capture d'écran de droite.

Étape 2 : Créez ViewHolder
- Ouvrez
SleepNightAdapter.kt. - Créez une classe dans
SleepNightAdapterappeléeViewHolderet faites-la étendreRecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}- Dans
ViewHolder, obtenez des références aux vues. Vous avez besoin d'une référence aux vues que ceViewHoldermettra à jour. Chaque fois que vous liez ceViewHolder, vous devez accéder à l'image et aux deux vues de texte. (Vous convertirez ce code pour utiliser la liaison de données ultérieurement.)
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)Étape 3 : Utiliser le ViewHolder dans SleepNightAdapter
- Dans la définition
SleepNightAdapter, au lieu deTextItemViewHolder, utilisezSleepNightAdapter.ViewHolderque vous venez de créer.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {Mettez à jour onCreateViewHolder() :
- Modifiez la signature de
onCreateViewHolder()pour renvoyerViewHolder. - Modifiez le programme d'inflation de la mise en page pour utiliser la ressource de mise en page appropriée,
list_item_sleep_night. - Supprimez le casting vers
TextView. - Au lieu de renvoyer un objet
TextItemViewHolder, renvoyez un objetViewHolder.
Voici la fonctiononCreateViewHolder()mise à jour :
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}Mettez à jour onBindViewHolder() :
- Modifiez la signature de
onBindViewHolder()pour que le paramètreholdersoit unViewHolderau lieu d'unTextItemViewHolder. - Dans
onBindViewHolder(), supprimez tout le code, à l'exception de la définition deitem. - Définissez un
resvalqui contient une référence auresourcespour cette vue.
val res = holder.itemView.context.resources- Définissez le texte de la vue de texte
sleepLengthsur la durée. Copiez le code ci-dessous, qui appelle une fonction de mise en forme fournie avec le code de démarrage.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)- Cela génère une erreur, car
convertDurationToFormatted()doit être défini. OuvrezUtil.kt, puis annulez la mise en commentaire du code et des importations associées. (Sélectionnez Code > Commenter avec des commentaires sur les lignes.) - De retour dans
onBindViewHolder(), utilisezconvertNumericQualityToString()pour définir la qualité.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)- Vous devrez peut-être importer manuellement ces fonctions.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString- Définissez l'icône correspondant à la qualité. La nouvelle icône
ic_sleep_activevous est fournie dans le code de démarrage.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})- Voici la fonction
onBindViewHolder()mise à jour, qui définit toutes les données pourViewHolder:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}- Exécutez votre application. L'écran devrait ressembler à la capture d'écran ci-dessous, avec l'icône de qualité du sommeil, ainsi que le texte indiquant la durée et la qualité du sommeil.

Votre RecyclerView est maintenant terminé. Vous avez appris à implémenter un Adapter et un ViewHolder, et vous les avez combinés pour afficher une liste avec un RecyclerView Adapter.
Le code que vous avez écrit jusqu'à présent montre comment créer un adaptateur et un support de vue. Toutefois, vous pouvez améliorer ce code. Le code permettant d'afficher et de gérer les porte-vues est mélangé, et onBindViewHolder() connaît les détails sur la façon de mettre à jour ViewHolder.
Dans une application de production, vous pouvez avoir plusieurs supports de vue, des adaptateurs plus complexes et plusieurs développeurs qui apportent des modifications. Vous devez structurer votre code de sorte que tout ce qui concerne un ViewHolder se trouve uniquement dans le ViewHolder.
Étape 1 : Refactoriser onBindViewHolder()
Dans cette étape, vous refactorisez le code et déplacez toutes les fonctionnalités du support de vue dans ViewHolder. L'objectif de cette refactorisation n'est pas de modifier l'apparence de l'application pour l'utilisateur, mais de permettre aux développeurs de travailler sur le code plus facilement et plus sûrement. Heureusement, Android Studio dispose d'outils pour vous aider.
- Dans
SleepNightAdapter, dansonBindViewHolder(), sélectionnez tout sauf l'instruction permettant de déclarer la variableitem. - Effectuez un clic droit, puis sélectionnez Refactor > Extract > Function (Refactoriser > Extraire > Fonction).
- Nommez la fonction
bindet acceptez les paramètres suggérés. puis sur OK.
La fonctionbind()est placée sousonBindViewHolder().
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}- Placez le curseur sur le mot
holderdu paramètreholderdebind(). Appuyez surAlt+Enter(Option+Entersur Mac) pour ouvrir le menu d'intention. Sélectionnez Convert parameter to receiver (Convertir le paramètre en récepteur) pour le convertir en fonction d'extension avec la signature suivante :
private fun ViewHolder.bind(item: SleepNight) {...}- Coupez et collez la fonction
bind()dansViewHolder. - Rendez
bind()public. - Si nécessaire, importez
bind()dans l'adaptateur. - Comme il se trouve désormais dans
ViewHolder, vous pouvez supprimer la partieViewHolderde la signature. Voici le code final de la fonctionbind()dans la classeViewHolder.
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}Étape 2 : Refactoriser onCreateViewHolder
La méthode onCreateViewHolder() de l'adaptateur gonfle actuellement la vue à partir de la ressource de mise en page pour ViewHolder. Toutefois, l'inflation n'a rien à voir avec l'adaptateur, mais tout à voir avec le ViewHolder. L'inflation doit se produire dans ViewHolder.
- Dans
onCreateViewHolder(), sélectionnez tout le code dans le corps de la fonction. - Effectuez un clic droit, puis sélectionnez Refactor > Extract > Function (Refactoriser > Extraire > Fonction).
- Nommez la fonction
fromet acceptez les paramètres suggérés. puis sur OK. - Placez le curseur sur le nom de la fonction
from. Appuyez surAlt+Enter(Option+Entersur Mac) pour ouvrir le menu d'intention. - Sélectionnez Déplacer vers l'objet associé. La fonction
from()doit se trouver dans un objet compagnon pour pouvoir être appelée sur la classeViewHolder, et non sur une instanceViewHolder. - Déplacez l'objet
companiondans la classeViewHolder. - Rendez
from()public. - Dans
onCreateViewHolder(), remplacez l'instructionreturnpour renvoyer le résultat de l'appel defrom()dans la classeViewHolder.
Vos méthodesonCreateViewHolder()etfrom()terminées devraient ressembler au code ci-dessous, et votre code devrait se compiler et s'exécuter sans erreur.
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}- Modifiez la signature de la classe
ViewHolderpour que le constructeur soit privé. Étant donné quefrom()est désormais une méthode qui renvoie une nouvelle instanceViewHolder, il n'y a plus aucune raison d'appeler le constructeur deViewHolder.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){- Exécutez l'application. Elle devrait se compiler et s'exécuter de la même manière qu'auparavant, ce qui est le résultat souhaité après la refactorisation.
Projet Android Studio : RecyclerViewFundamentals
- L'affichage d'une liste ou d'une grille de données est l'une des tâches d'UI les plus courantes dans Android.
RecyclerViewest conçu pour être efficace, même lors de l'affichage de très longues listes. RecyclerViewne traite ou ne dessine que les éléments actuellement visibles à l'écran.- Lorsqu'un élément disparaît de l'écran, ses vues sont recyclées. Cela signifie que l'élément affiche le nouveau contenu qui défile à l'écran.
- Le modèle d'adaptateur en génie logiciel permet à un objet de fonctionner avec une autre API.
RecyclerViewutilise un adaptateur pour transformer les données de l'application en un format qu'il peut afficher, sans avoir à modifier la façon dont l'application stocke et traite les données.
Pour afficher vos données dans un RecyclerView, vous avez besoin des éléments suivants :
- RecyclerView
: pour créer une instance deRecyclerView, définissez un élément<RecyclerView>dans le fichier de mise en page. - LayoutManager
: unRecyclerViewutilise unLayoutManagerpour organiser la mise en page des éléments duRecyclerView, par exemple en les disposant dans une grille ou dans une liste linéaire.
Dans le<RecyclerView>du fichier de mise en page, définissez l'attributapp:layoutManagersur le gestionnaire de mise en page (par exemple,LinearLayoutManagerouGridLayoutManager).
Vous pouvez également définir leLayoutManagerd'unRecyclerViewde manière programmatique. (Cette technique sera abordée dans un prochain atelier de programmation.) - Mise en page pour chaque élément
: créez une mise en page pour un élément de données dans un fichier de mise en page XML. - Adaptateur
: créez un adaptateur qui prépare les données et la façon dont elles seront affichées dans unViewHolder. Associez l'adaptateur àRecyclerView.
LorsqueRecyclerViews'exécute, il utilise l'adaptateur pour déterminer comment afficher les données à l'écran.
L'adaptateur vous oblige à implémenter les méthodes suivantes :
–getItemCount()pour renvoyer le nombre d'éléments.
–onCreateViewHolder()pour renvoyer leViewHolderd'un élément de la liste.
–onBindViewHolder()pour adapter les données aux vues d'un élément de la liste. - ViewHolder
: unViewHoldercontient les informations de vue pour afficher un élément de la mise en page de l'élément. - La méthode
onBindViewHolder()de l'adaptateur adapte les données aux vues. Vous remplacez toujours cette méthode. En règle générale,onBindViewHolder()développe la mise en page d'un élément et place les données dans les vues de la mise en page. - Étant donné que
RecyclerViewne sait rien des données,Adapterdoit informerRecyclerViewlorsque ces données changent. UtiliseznotifyDataSetChanged()pour notifier auAdapterque les données ont changé.
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
Comment RecyclerView affiche-t-il les éléments ? Plusieurs réponses possibles.
▢ Affiche les éléments sous forme de liste ou de grille.
▢ Faire défiler l'écran verticalement ou horizontalement.
▢ Possibilité de faire défiler l'écran en diagonale sur les appareils plus grands, comme les tablettes.
▢ Possibilité d'utiliser des mises en page personnalisées lorsqu'une liste ou une grille ne sont pas suffisantes pour le cas d'utilisation.
Question 2
Quels sont les avantages de RecyclerView ? Plusieurs réponses possibles.
▢ Affichage efficace des listes volumineuses.
▢ Les données sont automatiquement mises à jour.
▢ Moins d'actualisations nécessaires à chaque fois qu'un élément est modifié, supprimé ou ajouté à la liste.
▢ Réutilise la vue qui défile hors écran pour afficher l'élément suivant qui défile à l'écran.
Question 3
Pourquoi utiliser des adaptateurs ? Plusieurs réponses possibles.
▢ La séparation des tâches simplifie la modification et la phase de test du code.
▢ RecyclerView est agnostique par rapport aux données affichées.
▢ Les couches de traitement des données n'ont pas à se soucier de la manière dont les données seront affichées.
▢ L'application s'exécutera plus rapidement.
Question 4
Parmi les affirmations suivantes concernant les ViewHolder, lesquelles sont vraies ? Plusieurs réponses possibles.
▢ La mise en page ViewHolder est définie dans des fichiers de mise en page XML.
▢ Il existe un ViewHolder pour chaque unité de données de l'ensemble de données.
▢ Une RecyclerView peut contenir plusieurs ViewHolder.
▢ L'Adapter associe les données au ViewHolder.
Passez à la leçon suivante :