Utiliser RecyclerView pour afficher une liste déroulante

1. Avant de commencer

Réfléchissez un instant aux applications que vous utilisez fréquemment sur votre téléphone. Presque toutes disposent d'au moins une liste. L'écran de l'historique des appels, l'application Contacts et votre application de réseaux sociaux préférée affichent tous une liste de données. Comme le montre la capture d'écran ci-dessous, certaines de ces applications affichent une simple liste de mots ou d'expressions, tandis que d'autres affichent des éléments plus complexes, tels que des fiches contenant du texte et des images. Quel que soit le contenu, l'affichage d'une liste de données est l'une des tâches d'UI les plus courantes dans Android.

cf10a913f9db0ee4.png

Android fournit RecyclerView pour vous aider à créer des applications avec des listes. RecyclerView garantit une efficacité optimale, même avec de longues listes, grâce à la réutilisation (ou au recyclage) des vues qui ne sont plus affichées à l'écran. Lorsqu'un élément de liste n'est plus affiché à l'écran, RecyclerView réutilise cette vue pour l'élément suivant sur le point d'être affiché. Cela signifie que l'élément affiche le nouveau contenu qui défile à l'écran. Ce comportement RecyclerView vous fait gagner beaucoup de temps de traitement et garantit un défilement plus fluide des listes.

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.

dcf4599789b9c2a1.png

Dans cet atelier de programmation, vous allez créer l'application Affirmations. Il s'agit d'une application simple qui affiche dix affirmations positives sous forme de texte dans une liste déroulante. Ensuite, dans l'atelier de suivi, vous ajouterez une image inspirante à chaque affirmation et vous peaufinerez l'interface utilisateur de l'application.

Conditions préalables

  • Créer un projet à partir d'un modèle dans Android Studio.
  • Ajouter des ressources de chaîne à une application.
  • Définir une mise en page au format XML.
  • Comprendre les classes et l'héritage dans le langage Kotlin (y compris les classes abstraites).
  • Hériter d'une classe existante et remplacer ses méthodes.
  • Utiliser la documentation disponible sur developer.android.com pour les cours fournis par le framework Android.

Points abordés

  • Comment utiliser un RecyclerView pour afficher une liste de données.
  • Comment organiser votre code dans des packages.
  • Comment utiliser des adaptateurs avec RecyclerView pour personnaliser l'apparence d'un élément de liste.

Objectifs de l'atelier

  • Application qui affiche une liste de chaînes d'affirmation à l'aide d'un RecyclerView.

Ce dont vous avez besoin

  • Un ordinateur équipé d'Android Studio version 4.1 ou ultérieure.

2. Créer le projet

Créer un projet "Activité vide"

Avant de créer un projet, assurez-vous d'utiliser Android Studio 4.1 ou version ultérieure.

  1. Démarrez un nouveau projet Kotlin dans Android Studio à l'aide du modèle Activité vide.
  2. Saisissez Affirmations comme nom de l'application, com.example.affirmations comme nom de package, puis sélectionnez API niveau 19 en tant que version minimale du SDK.
  3. Cliquez sur Terminer pour créer le projet.

3. Configurer la liste de données

L'étape suivante du processus de création de l'application Affirmations consiste à ajouter des ressources. Vous allez ajouter les éléments suivants à votre projet.

  • Des ressources de chaîne à afficher comme affirmations dans l'application.
  • Une source de données pour fournir une liste d'affirmations à votre application.

Ajouter des chaînes d'affirmation

  1. Dans la fenêtre Projet, ouvrez le fichier app > res > values > strings.xml. Actuellement, ce fichier ne contient qu'une seule ressource, à savoir le nom de l'application.
  2. Dans strings.xml, ajoutez les affirmations suivantes en tant que ressources de chaîne individuelles. Nommez-les affirmation1, affirmation2, etc.

Texte des affirmations

I am strong.
I believe in myself.
Each day is a new opportunity to grow and be a better version of myself.
Every challenge in my life is an opportunity to learn from.
I have so much to be grateful for.
Good things are always coming into my life.
New opportunities await me at every turn.
I have the courage to follow my heart.
Things will unfold at precisely the right time.
I will be present in all the moments that this day brings.

Une fois que vous avez terminé, le fichier strings.xml doit se présenter comme suit.

<resources>
    <string name="app_name">Affirmations</string>
    <string name="affirmation1">I am strong.</string>
    <string name="affirmation2">I believe in myself.</string>
    <string name="affirmation3">Each day is a new opportunity to grow and be a better version of myself.</string>
    <string name="affirmation4">Every challenge in my life is an opportunity to learn from.</string>
    <string name="affirmation5">I have so much to be grateful for.</string>
    <string name="affirmation6">Good things are always coming into my life.</string>
    <string name="affirmation7">New opportunities await me at every turn.</string>
    <string name="affirmation8">I have the courage to follow my heart.</string>
    <string name="affirmation9">Things will unfold at precisely the right time.</string>
    <string name="affirmation10">I will be present in all the moments that this day brings.</string>
</resources>

Maintenant que vous avez ajouté des ressources de chaîne, vous pouvez les référencer dans votre code en tant que R.string.affirmation1 ou R.string.affirmation2.

Créer un package

En organisant votre code de manière logique, vous et d'autres développeurs pouvez le comprendre, le gérer et l'étendre. À l'instar des documents, que vous pouvez organiser en fichiers et dossiers, vous pouvez organiser votre code en fichiers et packages.

Qu'est-ce qu'un package ?

  1. Dans la fenêtre Project (Projet) (Android) d'Android Studio, examinez les nouveaux fichiers de projet sous app > java pour l'application Affirmations. Ils doivent ressembler à la capture d'écran ci-dessous qui présente trois packages : un pour votre code (com.example.affirmations) et deux pour les fichiers de test (com.example.affirmations (androidTest) et com.example.affirmations (test)).

809a0d77a0759dc5.png

  1. Notez que le nom du package comprend plusieurs mots séparés par un point.

Vous pouvez utiliser les packages de deux façons.

  • Créer différents packages pour les différentes parties de votre code. Par exemple, les développeurs séparent souvent dans des packages différents les classes qui utilisent des données de celles qui créent l'UI.
  • Utiliser le code d'autres packages. Pour utiliser les classes d'autres packages, vous devez les définir dans les dépendances de votre système de compilation. Vous pouvez également les importer (instruction import) dans votre code afin de pouvoir utiliser leurs noms abrégés (par exemple, TextView) au lieu de leurs noms complets (par exemple, android.widget.TextView). Par exemple, vous avez déjà utilisé des instructions import pour des classes telles que sqrt (import kotlin.math.sqrt) et View (import android.view.View).

Dans l'application Affirmations, en plus d'importer les classes Android et Kotlin, vous allez organiser votre application dans plusieurs packages. Même si votre application ne contient pas beaucoup de classes, il est recommandé d'utiliser des packages pour les regrouper par fonctionnalité.

Nommer des packages

Vous pouvez attribuer n'importe quel nom à un package, pour autant qu'il soit unique. En d'autres termes, aucun autre package publié ne peut porter le même nom. Compte tenu du très grand nombre de packages, et de la difficulté de trouver des noms uniques et aléatoires, les programmeurs utilisent des conventions pour faciliter la création et la compréhension des noms de package.

  • La structure du nom de package va du niveau général au plus spécifique. Chaque partie du nom est en minuscules et séparée par un point. Important : le point fait simplement partie du nom. Cela n'indique pas une hiérarchie dans le code et n'impose pas une structure de dossiers.
  • Les domaines Internet sont uniques à l'échelle mondiale. Par convention, on utilise donc un domaine, généralement le vôtre ou celui de votre entreprise, comme première partie du nom.
  • Vous pouvez choisir les noms des packages pour indiquer ce qu'ils contiennent, ainsi que les relations qu'ils entretiennent.
  • Pour les exemples de code tels que celui-ci, on utilise généralement com.example suivi du nom de l'application.

Voici quelques exemples de noms de packages prédéfinis et de leur contenu :

  • kotlin.math : constantes et fonctions mathématiques.
  • android.widget : vues, par exemple TextView.

Créer un package

  1. Dans le volet Projet d'Android Studio, effectuez un clic droit sur app > java > com.example.affirmations, puis sélectionnez Nouveau > Package.

39f35a81a574b7ee.png

  1. Dans le pop-up Nouveau package, notez le préfixe du nom de package suggéré. La première partie est le nom du package sur lequel vous avez effectué un clic droit. Bien que les noms de package ne créent pas de hiérarchie de packages, des parties du nom sont réutilisées pour indiquer une relation et une organisation du contenu.
  2. Dans le pop-up, ajoutez model à la fin du nom de package suggéré. Les développeurs utilisent souvent model comme nom de package pour les classes qui modélisent (ou représentent) les données.

3d392f8da53adc6f.png

  1. Appuyez sur Entrée. Un package est créé sous le package com.example.affirmations (racine). Ce nouveau package contiendra toutes les classes liées aux données définies dans votre application.

Créer la classe de données Affirmation

Dans cette tâche, vous allez créer une classe appelée Affirmation. Une instance d'objet de Affirmation représente une affirmation et contient l'ID de ressource de la chaîne avec l'affirmation en question.

  1. Effectuez un clic droit sur le package com.example.affirmations.model, puis sélectionnez Nouveau > Fichier/Classe Kotlin.

a6bc9eb5c382cf9.png

  1. Dans le pop-up, sélectionnez Classe, puis saisissez Affirmation comme nom de classe. Cette opération crée un fichier nommé Affirmation.kt dans le package model.
  2. Transformez Affirmation en classe de données en ajoutant le mot clé data avant la définition de classe. Vous obtiendrez une erreur, car au moins une propriété doit être définie pour les classes de données.

Affirmation.kt

package com.example.affirmations.model

data class Affirmation {
}

Lorsque vous créez une instance de Affirmation, vous devez transmettre l'ID de ressource de la chaîne d'affirmation. L'ID de ressource est un entier.

  1. Ajoutez un paramètre d'entier val stringResourceId au constructeur de la classe Affirmation. Cela permet d'éliminer l'erreur.
package com.example.affirmations.model

data class Affirmation(val stringResourceId: Int)

Créer une classe en tant que source de données

Les données affichées dans votre application peuvent provenir de différentes sources (par exemple, de votre projet d'application ou d'une source externe qui nécessite une connexion à Internet pour télécharger des données). Par conséquent, il se peut que les données ne soient pas exactement au format dont vous avez besoin. Le reste de l'application ne doit pas se préoccuper de la provenance ni du format d'origine des données. Vous pouvez (et c'est même conseillé) dissimuler cette préparation de données dans une classe Datasource distincte qui prépare les données pour l'application.

La préparation des données étant un problème distinct, placez la classe Datasource dans un package data séparé.

  1. Dans la fenêtre Projet d'Android Studio, effectuez un clic droit sur app > java > com.example.affirmations, puis sélectionnez Nouveau > Package.
  2. Saisissez data comme dernière partie du nom du package.
  3. Effectuez un clic droit sur le package data, puis sélectionnez Nouveau fichier/Nouvelle classe Kotlin.
  4. Saisissez Datasource comme nom de classe.
  5. Dans la classe Datasource, créez une fonction appelée loadAffirmations().

La fonction loadAffirmations() doit renvoyer une liste d'Affirmations. Pour ce faire, créez une liste et insérez-y une instance Affirmation pour chaque chaîne de ressource.

  1. Déclarez List<Affirmation> comme type renvoyé de la méthode loadAffirmations().
  2. Dans le corps de loadAffirmations(), ajoutez une instruction return.
  3. Après le mot clé return, appelez listOf<>() pour créer une List.
  4. Entre les chevrons <>, indiquez le type des éléments de liste en tant que Affirmation. Si nécessaire, importez com.example.affirmations.model.Affirmation.
  5. Entre parenthèses, créez une Affirmation en transmettant R.string.affirmation1 comme ID de ressource, comme indiqué ci-dessous.
Affirmation(R.string.affirmation1)
  1. Ajoutez les objets Affirmation restants à la liste de toutes les affirmations, en les séparant par des virgules. Une fois terminé, le code doit se présenter comme suit.

Datasource.kt

package com.example.affirmations.data

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource {

    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1),
            Affirmation(R.string.affirmation2),
            Affirmation(R.string.affirmation3),
            Affirmation(R.string.affirmation4),
            Affirmation(R.string.affirmation5),
            Affirmation(R.string.affirmation6),
            Affirmation(R.string.affirmation7),
            Affirmation(R.string.affirmation8),
            Affirmation(R.string.affirmation9),
            Affirmation(R.string.affirmation10)
        )
    }
}

[Facultatif] Afficher la taille de la liste Affirmations dans un élément TextView

Pour vérifier que vous pouvez créer une liste d'affirmations, vous pouvez appeler loadAffirmations() et afficher la taille de la liste d'affirmations renvoyée dans le TextView fourni avec votre modèle d'application "Activité vide".

  1. Dans layouts/activity_main.xml, attribuez l'id de textview au TextView fourni avec votre modèle.
  2. Dans MainActivity, dans la méthode onCreate() située après le code existant, obtenez une référence à textview.
val textView: TextView = findViewById(R.id.textview)
  1. Ajoutez ensuite du code pour créer et afficher la taille de la liste des affirmations. Créez un Datasource, appelez loadAffirmations(), obtenez la taille de la liste renvoyée, convertissez-la en chaîne et affectez-la en tant que text de textView.
textView.text = Datasource().loadAffirmations().size.toString()
  1. Exécutez votre application. Vous devriez alors obtenir l'écran ci-dessous.

b4005973d4a0efc8.png

  1. Supprimez le code que vous venez d'ajouter dans MainActivity.

4. Ajouter une RecyclerView à votre application

Dans cette tâche, vous allez configurer une RecyclerView de manière à afficher la liste Affirmations.

La création et l'utilisation d'une RecyclerView reposent sur un certain nombre d'éléments. On peut les considérer comme une division du travail. Le schéma ci-dessous vous donne une vue d'ensemble. Vous en apprendrez davantage sur chaque élément au fur et à mesure de l'implémentation.

  • élément : élément de données de la liste à afficher. Représente un objet Affirmation de votre application.
  • Adaptateur : accepte les données et les prépare pour que RecyclerView les affiche.
  • ViewHolders : pool de vues que RecyclerView peut utiliser et réutiliser pour afficher des affirmations.
  • RecyclerView : vues affichées à l'écran.

4e9c18b463f00bf7.png

Ajouter une RecyclerView à la mise en page

L'application Affirmations comporte une seule activité nommée MainActivity et son fichier de mise en page s'appelle activity_main.xml. Tout d'abord, vous devez ajouter RecyclerView à la mise en page de MainActivity.

  1. Ouvrez activity_main.xml (app > res > layouts > activity_main.xml).
  2. Si ce n'est pas encore le cas, passez en vue fractionnée.

7e7c3e8429267dac.png

  1. Supprimez la TextView.

La mise en page actuelle utilise ConstraintLayout. Particulièrement flexible, ConstraintLayout est la solution idéale lorsque vous souhaitez positionner plusieurs vues enfants dans une mise en page. Étant donné que votre mise en page ne comporte qu'une seule vue enfant, RecyclerView, vous pouvez basculer vers un ViewGroupplus simple, appelé FrameLayout, qui doit être utilisé pour contenir une seule vue enfant.

9e6f235be5fa31a8.png

  1. Dans le code XML, remplacez ConstraintLayout par FrameLayout. La mise en page doit se présenter comme suit.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="match_parent"
    tools:context=".MainActivity">
</FrameLayout>
  1. Passez à la vue Conception.
  2. Dans la Palette, sélectionnez Conteneurs, puis recherchez RecyclerView.
  3. Faites glisser une vue RecyclerView dans la mise en page.
  4. Si le pop-up Ajouter une dépendance de projet s'affiche, lisez-le, puis cliquez sur OK. (Si le pop-up ne s'affiche pas, aucune action n'est requise.)
  5. Attendez qu'Android Studio ait terminé et que la RecyclerView s'affiche dans votre mise en page.
  6. Si nécessaire, remplacez les attributs layout_width et layout_height de la RecyclerView par match_parent pour que la RecyclerView puisse remplir l'intégralité de l'écran.
  7. Définissez l'ID de ressource de RecyclerView sur recycler_view.

RecyclerView permet d'afficher des éléments de différentes manières ; sous la forme d'une liste linéaire ou d'une grille, par exemple. La disposition des éléments est gérée par un LayoutManager. Le framework Android fournit des gestionnaires de mise en page pour des mises en page d'éléments de base. L'application Affirmations affiche les éléments sous la forme d'une liste verticale, ce qui vous permet d'utiliser le LinearLayoutManager.

  1. Revenez à la vue Code. Dans l'élément RecyclerView du code XML, ajoutez LinearLayoutManager en tant qu'attribut de gestionnaire de mise en page de la RecyclerView, comme indiqué ci-dessous.
app:layoutManager="LinearLayoutManager"

Pour qu'il soit possible de faire défiler une liste verticale d'éléments plus longue que l'écran, vous devez ajouter une barre de défilement verticale.

  1. Dans RecyclerView, ajoutez un attribut android:scrollbars défini sur vertical.
android:scrollbars="vertical"

La mise en page XML finale doit ressembler à ceci :

activity_main.xml

<FrameLayout 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="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layoutManager="LinearLayoutManager" />

</FrameLayout>
  1. Exécutez votre application.

Le projet doit normalement être compilé et exécuté sans problème. Toutefois, seul un arrière-plan blanc s'affiche dans votre application, car il vous manque un extrait de code essentiel. Pour l'instant, vous disposez de la source de données et de la RecyclerView qui a été ajoutée à votre mise en page. Cependant, la RecyclerView ne possède aucune information quant à la façon d'afficher les objets Affirmation.

Implémenter un adaptateur pour RecyclerView

Votre application doit pouvoir récupérer les données de Datasource et les mettre en forme, de sorte que chaque Affirmation puisse être affichée en tant qu'élément dans RecyclerView.

L'adaptateur est un schéma de conception qui transforme les données en un élément utilisable par RecyclerView. Dans le cas présent, vous avez besoin d'un adaptateur qui récupère une instance Affirmation de la liste renvoyée par loadAffirmations() et la transforme en une vue d'élément de liste, de sorte qu'elle puisse être affichée dans RecyclerView..

Lorsque vous exécutez l'application, RecyclerView utilise l'adaptateur pour déterminer comment afficher vos données à l'écran. RecyclerView demande à l'adaptateur de créer une vue d'élément de liste pour le premier élément de données de la liste. Une fois la vue obtenue, il demande à l'adaptateur de fournir les données nécessaires pour dessiner l'élément. Ce processus se répète jusqu'à ce que RecyclerView n'ait plus besoin de vues pour remplir l'écran. Si seulement trois vues d'éléments de liste peuvent être affichées en même temps à l'écran, RecyclerView demande à l'adaptateur de ne préparer que ces trois vues (au lieu des 10 prévues).

Au cours de cette étape, vous allez créer un adaptateur qui adapte une instance d'objet Affirmation pour qu'elle puisse être affichée dans RecyclerView.

Créer l'adaptateur

Un adaptateur se compose de plusieurs parties. De plus, le code que vous allez écrire sera, en grande partie, plus complexe que celui que vous avez rédigé jusqu'à présent. Ne vous inquiétez pas si vous ne comprenez pas immédiatement tous les détails. Une fois que vous aurez terminé cette application avec une RecyclerView, vous comprendrez mieux comment toutes les parties s'intègrent les unes aux autres. Vous pourrez également réutiliser ce code comme base pour les prochaines applications que vous créerez avec une RecyclerView.

Créer une mise en page pour les éléments

Chaque élément de la RecyclerView possède sa propre mise en page, que vous définissez dans un fichier distinct. Étant donné que vous afficherez uniquement une chaîne, vous pouvez utiliser une TextView pour la mise en page de votre élément.

  1. Dans res > layouts, créez un fichier vide nommé list_item.xml.
  2. Ouvrez list_item.xml dans la vue Code.
  3. Ajoutez une TextView avec id item_title.
  4. Ajoutez wrap_content pour layout_width et layout_height, comme indiqué dans le code ci-dessous.

Notez qu'il n'est pas nécessaire d'utiliser un ViewGroup autour de votre mise en page, car cette mise en page d'élément de liste sera, par la suite, gonflée et ajoutée en tant qu'enfant à l'élément RecyclerView parent.

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Vous pouvez également utiliser Fichier > Nouveau > Fichier de ressources de mise en page, en indiquant list_item.xml dans le champ Nom de fichier et TextView dans le champ Élément racine. Ensuite, mettez à jour le code généré pour qu'il corresponde au code ci-dessus.

dbb34ca516c804c6.png

Créer une classe ItemAdapter

  1. Dans le volet Projet d'Android Studio, effectuez un clic droit sur app > java > com.example.affirmations, puis sélectionnez Nouveau > Package.
  2. Saisissez adapter comme dernière partie du nom du package.
  3. Effectuez un clic droit sur le package adapter, puis sélectionnez Nouveau > Fichier/Classe Kotlin.
  4. Saisissez ItemAdapter comme nom de classe, terminez l'opération et le fichier ItemAdapter.kt s'ouvre.

Vous devez ajouter un paramètre au constructeur de ItemAdapter afin de pouvoir transmettre la liste d'affirmations à l'adaptateur.

  1. Ajoutez au constructeur ItemAdapter un paramètre val nommé dataset, de type List<Affirmation>. Si nécessaire, importez Affirmation.
  2. Étant donné que le dataset ne sera utilisé que dans cette classe, définissez-le sur private.

ItemAdapter.kt

import com.example.affirmations.model.Affirmation

class ItemAdapter(private val dataset: List<Affirmation>) {

}

ItemAdapter a besoin d'informations sur la façon de résoudre les ressources de chaîne. Ces informations, ainsi que d'autres concernant l'application, sont stockées dans une instance d'objet Context que vous pouvez transmettre à une instance ItemAdapter.

  1. Ajoutez au constructeur ItemAdapter un paramètre val nommé context, de type Context. Positionnez-le comme premier paramètre du constructeur.
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {

}

Créer un Viewholder

RecyclerView n'interagit pas directement avec les vues d'élément, mais traite plutôt avec ViewHolders. Un ViewHolder représente une vue d'élément de liste unique dans RecyclerView et peut être réutilisée lorsque cela s'avère possible. Une instance ViewHolder contient des références à chacune des vues d'une mise en page d'élément de liste (d'où son nom qui signifie "conteneur de vues"). Il est ainsi plus facile de mettre à jour la vue d'élément de liste avec de nouvelles données. Les conteneurs de vues ajoutent également des informations qui sont utilisées par RecyclerView pour déplacer efficacement des vues sur l'écran.

  1. Dans la classe ItemAdapter, avant l'accolade fermante pour ItemAdapter, créez une classe ItemViewHolder.
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {

    class ItemViewHolder()
}
  • Lorsque vous définissez une classe à l'intérieur d'une autre, on parle de création d'une classe imbriquée.
  • Étant donné que ItemViewHolder n'est utilisé que par ItemAdapter, le fait de le créer à l'intérieur de ItemAdapter montre cette relation. Cette opération est facultative, mais elle permet aux autres développeurs de comprendre la structure de votre programme.
  1. Ajoutez une private val view de type View en tant que paramètre au constructeur de classe ItemViewHolder.
  2. Définissez ItemViewHolder comme sous-classe de RecyclerView.ViewHolder et transmettez le paramètre view au constructeur de super-classe.
  3. Dans ItemViewHolder, définissez une propriété val textView de type TextView. Attribuez-lui la vue avec l'ID item_title que vous avez défini dans list_item.xml.
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {

    class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.item_title)
    }
}

Remplacer les méthodes d'adaptateur

  1. Ajoutez le code pour étendre votre ItemAdapter à partir de la classe abstraite RecyclerView.Adapter. Indiquez ItemAdapter.ItemViewHolder comme type de conteneur de vues entre chevrons.
class ItemAdapter(
    private val context: Context,
    private val dataset: List<Affirmation>
) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

    class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.item_title)
    }
}

Une erreur s'affiche, car vous devez implémenter des méthodes abstraites à partir de RecyclerView.Adapter.

  1. Placez votre curseur sur ItemAdapter et appuyez sur Commande+I (Ctrl+I sous Windows). La liste des méthodes à implémenter (getItemCount(), onCreateViewHolder() et onBindViewHolder()) est affichée.

7a8a383a8633094b.png

  1. Sélectionnez les trois fonctions en utilisant Maj+clic, puis cliquez sur OK.

Des bouchons sont alors créés avec les paramètres appropriés pour les trois méthodes, comme indiqué ci-dessous.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
    TODO("Not yet implemented")
}

override fun getItemCount(): Int {
    TODO("Not yet implemented")
}

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
    TODO("Not yet implemented")
}

Il ne devrait plus y avoir d'erreurs. Vous devez ensuite implémenter ces méthodes afin qu'elles fonctionnent correctement pour votre application.

Implémenter getItemCount()

La méthode getItemCount() doit renvoyer la taille de votre ensemble de données. Les données de votre application se trouvent dans la propriété dataset que vous transmettez au constructeur ItemAdapter. Vous pouvez obtenir leur taille avec size.

  1. Remplacez getItemCount() par le code suivant :
override fun getItemCount() = dataset.size

Il s'agit d'une manière plus concise d'écrire ceci :

override fun getItemCount(): Int {
    return dataset.size
}

Implémenter onCreateViewHolder()

La méthode onCreateViewHolder() est appelée par le gestionnaire de mises en page afin de créer des conteneurs de vues pour la RecyclerView (s'il n'existe aucun conteneur de vues pouvant être réutilisé). N'oubliez pas qu'un conteneur de vues représente une seule vue d'élément de liste.

La méthode onCreateViewHolder() utilise deux paramètres et renvoie un nouveau ViewHolder.

  • Un paramètre parent, qui est le groupe de vues auquel la nouvelle vue d'élément de liste sera associée en tant qu'enfant. La vue parente est la RecyclerView.
  • Un paramètre viewType, qui devient important lorsqu'une même RecyclerView contient plusieurs types de vue d'élément. Si différentes mises en page d'élément de liste sont affichées dans la RecyclerView, il existe différents types de vue d'élément. Vous ne pouvez recycler que des vues d'élément ayant le même type. Dans votre cas, il n'existe qu'une seule mise en page d'élément de liste et un seul type de vue d'élément. Vous ne devez donc pas vous soucier de ce paramètre.
  1. Dans la méthode onCreateViewHolder(), obtenez une instance de LayoutInflater à partir du contexte fourni (context du parent). Le système de gonflage de mise en page sait comment gonfler une mise en page XML dans une hiérarchie d'objets de vue.
val adapterLayout = LayoutInflater.from(parent.context)
  1. Une fois que vous disposez d'une instance d'objet LayoutInflater, ajoutez un point suivi d'un autre appel de méthode pour gonfler la vue d'élément de liste. Transmettez l'ID de ressource de mise en page XML R.layout.list_item et le groupe de vues parent. Le troisième argument booléen est attachToRoot. Cet argument doit être false, car RecyclerView ajoute automatiquement cet élément à la hiérarchie des vues le moment venu.
val adapterLayout = LayoutInflater.from(parent.context)
       .inflate(R.layout.list_item, parent, false)

adapterLayout contient désormais une référence à la vue de l'élément de liste (dans laquelle nous trouverons par la suite

des vues enfants telles que la TextView).

  1. Dans onCreateViewHolder(), renvoyez une nouvelle instance ItemViewHolder où la vue racine est adapterLayout.
return ItemViewHolder(adapterLayout)

Voici le code qui a été écrit jusqu'ici pour onCreateViewHolder().

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
    // create a new view
    val adapterLayout = LayoutInflater.from(parent.context)
        .inflate(R.layout.list_item, parent, false)

    return ItemViewHolder(adapterLayout)
}

Implémenter onBindViewHolder()

La dernière méthode que vous devez remplacer est onBindViewHolder(). Cette méthode est appelée par le gestionnaire de mise en page afin de remplacer le contenu d'une vue d'élément de liste.

La méthode onBindViewHolder() comporte deux paramètres : un ItemViewHolder créé précédemment par la méthode onCreateViewHolder() et un int représentant la position actuelle de l'élément dans la liste. Cette méthode vous permet de trouver l'objet Affirmation approprié dans l'ensemble de données en fonction de la position.

  1. Dans onBindViewHolder(), créez un val item et récupérez l'élément au niveau de la position indiquée dans le dataset.
val item = dataset[position]

Enfin, vous devez mettre à jour toutes les vues référencées par le conteneur de vues afin de tenir compte des données correctes pour cet élément. Dans le cas présent, il n'y a qu'une seule vue : TextView dans ItemViewHolder. Définissez le texte de TextView pour afficher la chaîne Affirmation de cet élément.

  1. Vous pouvez, avec une instance d'objet Affirmation, trouver l'ID de ressource de chaîne correspondant en appelant item.stringResourceId. Toutefois, il s'agit d'un entier et vous devez trouver la correspondance avec la valeur de chaîne réelle.

Dans le framework Android, vous pouvez appeler getString() avec un ID de ressource de chaîne pour renvoyer la valeur de chaîne qui y est associée. getString() est une méthode de la classe Resources. Vous pouvez obtenir une instance de la classe Resources via context.

Cela signifie que vous pouvez appeler context.resources.getString() et transmettre un ID de ressource de chaîne. La chaîne obtenue peut être définie comme text de textView dans le holder ItemViewHolder. En résumé, cette ligne de code met à jour le conteneur de vues pour afficher la chaîne d'affirmation.

holder.textView.text = context.resources.getString(item.stringResourceId)

La méthode onBindViewHolder() terminée doit se présenter comme suit :

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
    val item = dataset[position]
    holder.textView.text =  context.resources.getString(item.stringResourceId)
}

Voici le code d'adaptateur terminé.

ItemAdapter.kt

package com.example.affirmations.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

/**
 * Adapter for the [RecyclerView] in [MainActivity]. Displays [Affirmation] data object.
 */
class ItemAdapter(
    private val context: Context,
    private val dataset: List<Affirmation>
) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder.
    // Each data item is just an Affirmation object.
    class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.item_title)
    }

    /**
     * Create new views (invoked by the layout manager)
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        // create a new view
        val adapterLayout = LayoutInflater.from(parent.context)
            .inflate(R.layout.list_item, parent, false)

        return ItemViewHolder(adapterLayout)
    }

    /**
     * Replace the contents of a view (invoked by the layout manager)
     */
    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = dataset[position]
        holder.textView.text = context.resources.getString(item.stringResourceId)
    }

    /**
     * Return the size of your dataset (invoked by the layout manager)
     */
    override fun getItemCount() = dataset.size
}

Maintenant que vous avez implémenté ItemAdapter, vous devez demander à RecyclerView d'utiliser cet adaptateur.

Modifier MainActivity pour utiliser une RecyclerView

Pour terminer, vous devez utiliser vos classes Datasource et ItemAdapter pour créer et afficher des éléments dans RecyclerView. Cette opération est effectuée dans MainActivity.

  1. Ouvrez MainActivity.kt.
  2. Dans MainActivity,, accédez à la méthode onCreate(). Insérez le nouveau code décrit dans les étapes suivantes après l'appel vers setContentView(R.layout.activity_main)..
  3. Créez une instance de Datasource et appelez la méthode loadAffirmations() sur cette instance. Stockez la liste d'affirmations renvoyée dans un val nommé myDataset.
val myDataset = Datasource().loadAffirmations()
  1. Créez une variable nommée recyclerView et utilisez findViewById() pour trouver une référence à RecyclerView dans la mise en page.
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
  1. Pour indiquer à recyclerView d'utiliser la classe ItemAdapter que vous avez créée, créez une instance ItemAdapter. ItemAdapter attend deux paramètres : le contexte (this) de cette activité et les affirmations dans myDataset.
  2. Affectez l'objet ItemAdapter à la propriété adapter de la recyclerView.
recyclerView.adapter = ItemAdapter(this, myDataset)
  1. Étant donné que la taille de mise en page de votre RecyclerView est fixe dans la mise en page de l'activité, vous pouvez définir le paramètre setHasFixedSize de RecyclerView sur true. Ce paramètre ne sert qu'à améliorer les performances. Utilisez ce paramètre si vous savez que les modifications apportées au contenu n'ont pas d'incidence sur la taille de mise en page de RecyclerView.
recyclerView.setHasFixedSize(true)
  1. Lorsque vous avez terminé, le code de MainActivity doit se présenter comme suit.

MainActivity.kt

package com.example.affirmations

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.adapter.ItemAdapter
import com.example.affirmations.data.Datasource

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Initialize data.
        val myDataset = Datasource().loadAffirmations()

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.adapter = ItemAdapter(this, myDataset)

        // Use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true)
    }
}
  1. Exécutez votre application. Une liste de chaînes d'affirmation doit normalement être affichée à l'écran.

427c10d4f29d769d.png

Félicitations ! Vous venez de créer une application qui affiche une liste de données avec RecyclerView et un adaptateur personnalisé. Prenez le temps d'examiner le code que vous avez créé et de comprendre l'interaction entre les différents éléments.

Cette application contient tous les éléments nécessaires pour afficher vos affirmations, mais elle n'est pas tout à fait prête pour la production. L'interface utilisateur pourrait être améliorée. Dans le prochain atelier de programmation, vous apprendrez à ajouter des images à l'application et à peaufiner l'interface utilisateur.

5. Code de solution

Le code de solution de cet atelier de programmation figure dans le projet et le module ci-dessous. Notez que certains fichiers Kotlin se trouvent dans des packages différents, comme indiqué par l'instruction package au début du fichier.

res/values/strings.xml

<resources>
    <string name="app_name">Affirmations</string>
    <string name="affirmation1">I am strong.</string>
    <string name="affirmation2">I believe in myself.</string>
    <string name="affirmation3">Each day is a new opportunity to grow and be a better version of myself.</string>
    <string name="affirmation4">Every challenge in my life is an opportunity to learn from.</string>
    <string name="affirmation5">I have so much to be grateful for.</string>
    <string name="affirmation6">Good things are always coming into my life.</string>
    <string name="affirmation7">New opportunities await me at every turn.</string>
    <string name="affirmation8">I have the courage to follow my heart.</string>
    <string name="affirmation9">Things will unfold at precisely the right time.</string>
    <string name="affirmation10">I will be present in all the moments that this day brings.</string>
</resources>

affirmations/data/Datasource.kt

package com.example.affirmations.data

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource {

    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1),
            Affirmation(R.string.affirmation2),
            Affirmation(R.string.affirmation3),
            Affirmation(R.string.affirmation4),
            Affirmation(R.string.affirmation5),
            Affirmation(R.string.affirmation6),
            Affirmation(R.string.affirmation7),
            Affirmation(R.string.affirmation8),
            Affirmation(R.string.affirmation9),
            Affirmation(R.string.affirmation10)
        )
    }
}

affirmations/model/Affirmation.kt

package com.example.affirmations.model

data class Affirmation(val stringResourceId: Int)

affirmations/MainActivty.kt

package com.example.affirmations

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.adapter.ItemAdapter
import com.example.affirmations.data.Datasource

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Initialize data.
        val myDataset = Datasource().loadAffirmations()

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.adapter = ItemAdapter(this, myDataset)

        // Use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true)
    }
}

affirmations/adapter/ItemAdapter.kt

package com.example.affirmations.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

/**
 * Adapter for the [RecyclerView] in [MainActivity]. Displays [Affirmation] data object.
 */
class ItemAdapter(
    private val context: Context,
    private val dataset: List<Affirmation>
) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder.
    // Each data item is just an Affirmation object.
    class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.item_title)
    }

    /**
     * Create new views (invoked by the layout manager)
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        // create a new view
        val adapterLayout = LayoutInflater.from(parent.context)
            .inflate(R.layout.list_item, parent, false)

        return ItemViewHolder(adapterLayout)
    }

    /**
     * Replace the contents of a view (invoked by the layout manager)
     */
    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = dataset[position]
        holder.textView.text = context.resources.getString(item.stringResourceId)
    }

    /**
     * Return the size of your dataset (invoked by the layout manager)
     */
    override fun getItemCount() = dataset.size
}

src/main/res/layout/activty_main.xml

<FrameLayout 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="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layoutManager="LinearLayoutManager" />

</FrameLayout>

src/main/res/layout/list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

6. Résumé

  • Le widget RecyclerView vous aide à afficher une liste de données.
  • RecyclerView utilise le schéma d'adaptateur pour adapter et afficher les données.
  • ViewHolder crée et stocke les vues pour RecyclerView.
  • RecyclerView intègre des LayoutManagers. RecyclerView délègue la disposition des éléments à LayoutManagers.

Pour implémenter l'adaptateur :

  • Créez une classe pour l'adaptateur ; ItemAdapter, par exemple.
  • Créez une classe ViewHolder personnalisée qui représente une seule vue d'élément de liste. Effectuez une extension à partir de la classe RecyclerView.ViewHolder.
  • Modifiez la classe ItemAdapter pour l'étendre à partir de RecyclerView.Adapter avec la classe ViewHolder personnalisée.
  • Implémentez les méthodes suivantes dans l'adaptateur : getItemsCount(), onCreateViewHolder() et onBindViewHolder().

7. En savoir plus