Le RecycleView en Kotlin en 4 étapes

Bonjour à tous ! Aujourd’hui je vous présente un élément incontournable pour faire vos listes, j’ai nommé le RecycleView. Il a beaucoup de similitudes avec le Listview. Si vous êtes curieux de savoir pourquoi, je vous invite à lire mon article sur la notion de ListView en Kotlin, en cliquant ici.

Dans cet article, je vous présenterai le RecycleView. J’exposerai ses similitudes et ces différences avec le ListView. Puis Je vous expliquerai en quatre étapes comment le mettre en place. Pour finir je terminerai avec quelques notions pour aller plus loin.

Qu’est ce qu’un RecycleView ?

  • C’est un conteneur, il va contenir une liste d’éléments.
  • Un élément est défini à partir d’un modèle xml que vous pouvez personnaliser.
  • Chaque élément va contenir une donnée ou un ensemble de données qu’on souhaite lister.
  • Le modèle est configuré et ajuster à partir de adaptateur du RecycleView
  • L’adaptateur peut être implémenté par vous afin qu’il corresponde à votre modèle ou bien vous pouvez le laisser par défaut.

Un RecycleView est une version plus optimisée et plus évoluée du ListView :

  • A la création du RecycleView chaque élément est créés et chargés en mémoire les éléments visibles à l’écran.
  • Lorsque l’utilisateur va faire défiler l’écran, des nouveaux éléments sont chargés en mémoire et viennent s’afficher à l’écran.
  • Lorsque un élément n’est plus visible à l’écran son espace mémoire peut être réutilisée par un autre élément. Ce qui permet d’optimiser l’application et d’éviter d’avoir trop d’éléments chargés en mémoire.

La mise en place du RecycleView

J’ai mis en place une application simple, elle affiche une liste d’animaux avec leur type (carnivores, herbivores, etc..), leur nom et leur image. Lorsque l’utilisateur clique sur un des éléments un message s’affiche en bas indiquant le nom de l’animal qui a été sélectionné.

Le mieux c’est de vous le présenter sous forme de démo. C’est à partir de cette application que je me suis basé pour rédiger mon article.

1ère étape : La création de votre RecycleView

C’est l’étape est la plus simple que vous allez pouvoir effectuer. Pour faire simple, ouvrez votre fichier main_activity.xml et ajouter votre recycleView entre les balises.  (Comme dans le code ci-dessous).

[Code]

Voilà, vous venez d’ajouter votre conteneur. Vous allez pouvoir ajouter des éléments à cette liste dès que vous aurez terminé les étapes deux et trois. L’identifiant (android:id) va vous permettre par la suite d’interagir avec votre RecycleView. le paramètre (android:layout_height=”match_parent”) va indiquer à votre application que votre composant va prendre en toute  la hauteur de votre écran. Le paramètre (android:layout_width=”match_parent”) va lui indiquer à votre application que votre RecylcleView va prendre toute la largeur de l’écran.

2éme étape : La présentation de vos éléments

Passons à l’étape de la création, si vous avez un esprit créatif vous allez aimer cette partie.  Lorsque je commence, j’ai une idée précise de là où je veux emmener l’application.  Je pars d’abord sur la structure et les zones qui vont contenir des composants. J’aime utiliser les LinearLayout car je les trouve simples à utiliser et ils me permettent d’orienter verticalement ou horizontalement mes zones. Puis une fois mes zones définies, j’ajoute mes composants dans les différentes zones. Il m’arrive également de modifier mon idée de départ pendant la réalisation en ajoutant les fonctionnalités pas prévues qui me passe par la tête.

Maintenant je vais vous montrer mon modèle qui servira à la présentation de vos éléments. Vous pouvez suivre cette étape en reproduisant mon design ou simplement le faire à votre sauce. Ne vous inquiétez pas, j’expliquerai dans les prochaines étapes comment vous pourrez raccrocher le wagon, si vous partez sur votre propre création.

Chaque élément de mon RecycleView sera présenté  comme mon modèle ci-dessous :

modele recycleView

Pour copier mon modèle, commencé par insérer les différentes zones (voir le code ci-dessous). Un linearLayout vertical (android:orientation) qui sera la couche principale qui contiendra un composant “view” et les autres couches inférieurs. La seconde couche est définie de manière horizontale afin de créer trois nouvelles zones qui contiendront mes composants (zone 1, zone 2 et zone 3).

La structure
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2"
android:orientation="horizontal">
<!--Zone 1 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
</LinearLayout>
<!--Zone 2 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:orientation="vertical">
</LinearLayout>
<!--Zone 3 -->
<LinearLayout android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
</LinearLayout>

</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000000" />
</LinearLayout>

Pour définir l’orientation d’un LinearLayout, il faut utiliser le paramètre “android:orientation”. Pour que les composants soient centrés, il faut utiliser le paramètre “android:layout_gravity”. Le paramètre “android:weightSum”  va permettre d’ajouter un poids à un notre composant pour qu’il s’adapte au mieux à n’importe quel écran de smartphone, tablette, ect…  Si vous souhaitez plus de détails je vous invite à consulter mon article “Comment adapter son application au format de l’écran ?”.

La structure c’est bien, mais il faut ajouter nos composants :

La présentation complète
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2"
android:orientation="horizontal">
<!--Zone 1 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView

android:text="Nom:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_margin="10dp"/>
<TextView

android:text="Type :"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>

</LinearLayout>
<!--Zone 2 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/nom_animal"
android:text="XXX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>
<TextView
android:id="@+id/type"
android:text="carnivore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>

</LinearLayout>
<!--Zone 3 -->
<LinearLayout android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/portrait"/>
</LinearLayout>

</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000000" />
</LinearLayout>

La zone 1  : servira à nommer les catégories (nom et type), elle ne sera pas évolutif en fonction des éléments qu’elle possédera. Dans cette zone les deux catégories vont être définies par deux TextView. Le paramètre (android:text) va nous permettre de remplir la valeur du texte. Notre texte restera le même, peu importe l’élément de notre liste d’où l’absence du paramètre “android:id”. Ce paramètre n’est pas utile car on ne souhaite pas interagir avec ce TextView dans notre application. Le paramètre (android:textStyle) permet de définir un style au texte (gras, souligné, italique ). Le paramètre (layout_margin) permet de rajouter une marge autour du composant (valeur en dp).

La zone 2 : va contenir les valeurs de nos catégories (prenant par exemple; Nom: Lion, Type : Carnivore),  Ces valeurs vont être évolutif en fonction des données que l’ont leurs transmet. Dans cette zone on va ajouter aussi deux TextView qui seront nos valeur. Le paramètre “android:id” permettra par la suite de pouvoir remplir les TextView avec nos futures valeurs.

La zone 3 : va posséder l’image correspondant à nos données (exemple du lion –> une image de Lion). Cette image va être représentée par une imageView. Cette imageView possède un identifiant  “android:id” pour lui associer une image par la suite.

3ème étape : La création de votre adaptateur

L’adaptateur est nécessaire car il va permettre de convertir un ensemble de données en un élément de notre recycleView. Pour créer l’adaptateur quelques mesures s’imposent :

Il faut créer un nouveau fichier Kotlin  : clique droit sur le dossier où se trouvent vos fichiers kotlin –> New –> Kotlin  File/Class.

Dans ce fichier, il faut créer trois classes une classe data, une classe qui hérite de la classe RecyclerView.Adapter<>() et une classe qui hérite de la classe RecyclerView.ViewHolder().

La classe Data

Une classe data est une classe de données où un objet, elle servira d’instance. C’est à partir de cette classe qu’on va venir créer nos différents items de notre liste. Pour que vous compreniez , je vais schématiser prenons l’exemple d’un pompier. Un pompier, il possède un nom, un prénom, une taille, un rôle au sein de la caserne, ect… Maintenant on souhaite avoir la liste de l’ensemble des pompiers d’une caserne. Pour avoir cette liste, il va falloir venir créer autant de pompiers qu’il y a de personne dans la caserne. Chaque pompier est différent, il faut donc les différencier par  leurs données personnelles. Lorsque qu’on va venir ajouter un pompier à la caserne, on va l’ajouter avec ces données à la caserne. On parle alors d’instance quand on va créer un pompier.

Pour déclarer une classe data en Kotlin c’est simple, il faut utiliser le mot-clé “data” devant “class” puis en paramètre de rajouter le type de nos attributs (données).

data class Animaux

Notre instance  représente  un animal, un animal  possède un identifiant, un nom , un type et un portrait (une image). Chaque élément de notre RecycleView va donc posséder un identifiant, un nom, un type et une image. Si vous avez rajouté d’autre composant à votre modèle (dans l’étape 2) et que vous souhaiter qu’il soit propre à un élément, il faudra alors rajouter des paramètres à cette classe.

Pour une image : rajouter un paramètre de type entier (exemple –> “nom de votre paramètre”: Int)

Pour du texte / titre : rajouter un paramètre de type string (exemple –> “nom de votre paramètre”: String)

Je ne vais pas tous les faire,  je pense que vous avez compris le principe.

La Classe Adapter

Cette classe va hériter de la classe RecyclerView.Adapter<>() . Il faut donc redéfinir ces méthodes. Les méthodes à redéfinir sont :

Adapter
class AnimauxAdapter(val AnimauxAfficher : Array<Animaux>,val listener: (Animaux) -> Unit)
: RecyclerView.Adapter<AnimauxAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
val ma_ligne = LayoutInflater.from(parent.context).inflate(R.layout.ma_ligne,parent, false)
return ViewHolder(ma_ligne)
}

override fun getItemCount(): Int = AnimauxAfficher.size

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.i("XXX","onBindViewHolder")
holder.bind(AnimauxAfficher[position],listener)
}

class ViewHolder(val elementDeListe : View) : RecyclerView.ViewHolder(elementDeListe)
{
fun bind(animaux: Animaux, listener: (Animaux) -> Unit) = with(itemView)
{
Log.i("XXX","bind")
itemView.nom_animal.text = animaux.nom
itemView.type.text = animaux.type
itemView.portrait.setImageResource(animaux.portrait)
setOnClickListener { listener(animaux) }
}
}

}

La méthode  “onBindViewHolder” : Cette méthode va permettre de mettre à jour notre recycleView avec l’élément à la position donnée en paramètre. Afin d’avoir de charger en mémoire seulement les éléments affiché à l’écran.

La méthode “getItemCount()” : Cette fonction va retourner le nombre total d’élément de notre recycleView.

La méthode “onCreateViewHolder” : Cette méthode va venir créer et afficher chaque élément à partir de notre modèle là, en retournant la classe ”ViewHolder”. Cette classe  viendra convertir nos données en éléments de notre recycleView.

La classe ViewHolder

Cette classe est codée dans notre classe adapter. Elle hérite de la classe RecyclerView.ViewHolder(). Il faut donc redéfinir la méthode “blind” de cette classe. Cette méthode va venir associer nos données de notre instance (classe data) avec les composants de notre modèle. C’est dans cette méthode que vous allez rajouter ou modifier les lignes de code afin qu’il corresponde à votre modèle. la méthode demande en paramètre notre classe data et un Listener. Chaque élément de notre classe data doit être liée à un composant sur le modèle. Le “listener” permettra de sélectionner un élément de notre liste.

Pour associer un composant à une donnée, il faut faire comme ci-dessous :

ViewHolder
class ViewHolder(val elementDeListe : View) : RecyclerView.ViewHolder(elementDeListe)
{
fun bind(animaux: Animaux, listener: (Animaux) -> Unit) = with(itemView)
{
Log.i("XXX","bind")
itemView.nom_animal.text = animaux.nom
itemView.type.text = animaux.type
itemView.portrait.setImageResource(animaux.portrait)
setOnClickListener { listener(animaux) }
}
}

Pour associer par exemple notre TextView à la donnée Lion, on cherche notre composant de notre modèle avec “itemView”, puis on indique que c’est le composant avec l’identifiant “nom_animal” (android:id). Comme on cherche à changer son texte on lui indique “text”. Pour terminer on lui ajoute la valeur à partir de notre instance. “Lion” correspond à une instance, “Loup” correspond à une autre instance.

4ème et dernière étape : La mise en place dans l’activité

Dans l’activité, je vais venir créer mon recycleView, l’initialiser puis lui insérer un ensemble de données.

MainActivity
class MainActivity : AppCompatActivity() {

lateinit var monRecycler: RecyclerView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//bouchon
val animaux = CreerMesLigne()

monRecycler = findViewById(R.id.RecycleView)
monRecycler.layoutManager = LinearLayoutManager(this)
monRecycler.adapter = AnimauxAdapter(animaux.toTypedArray())
{
Toast.makeText(this,"Vous avez sélectionné ${it.nom}",Toast.LENGTH_SHORT).show()
}
}
fun CreerMesLigne() :ArrayList<Animaux>
{
val animaux = ArrayList<Animaux>()
animaux.add(Animaux(1,"Loup","Carnivore",R.drawable.wolf))
animaux.add(Animaux(2,"Gorille","Folivore et Frugivore",R.drawable.gorilla))
animaux.add(Animaux(3,"Eléphant","Herbivore ",R.drawable.elephant))
animaux.add(Animaux(4,"Tigre","Carnivore ",R.drawable.tiger))
animaux.add(Animaux(5,"Dauphin","Carnivore ",R.drawable.dolphin))
animaux.add(Animaux(6,"Lion","Carnivore ",R.drawable.lion))
animaux.add(Animaux(7,"Panthère noire","Carnivore ",R.drawable.panther))
animaux.add(Animaux(8,"Requin Blanc","Carnivore ",R.drawable.shark))
animaux.add(Animaux(9,"Pygargue à tête blanche","Carnivore ",R.drawable.bald_eagle))
return animaux
}
}

Pour créer un ensemble de données, j’ai créé la fonction “CreerMesLigne” pour cet usage. Dans cette méthode je viens ajouter dans un arrayList (tableau d’objet) un bon nombre d’instances créer à partir de ma classe data. J’ajoute à cette arraylist avec la méthode “add“,  mes différentes instances avec leurs données personnelles.

Dans la méthode onCreateView, j’initialise l’adaptateur de mon recylceView par mon adapter de mon autre  fichier (celui que vous avez développé). Je  transmets mon arrayList d’instance généré par la méthode  “CreerMesLigne” à mon adapter.

Pour aller plus loin

Pour aller plus loin,  vous pouvez améliorer l’application en utilisant FireBase afin d’éviter de générer vous même vos données à chaque lancement de l’application. FireBase est un serveur de base de données mis en place par google qui propose beaucoup de fonctionnalités intéressantes. Vous pouvez incorporer une base de données en utilisant une api pour vous y connecter et venir requêter cette base avec l’API.

Merci d’avoir lu ! Maintenant, si vous avez aimé le contenu de l’article, si  vous connaissez une personne à qui cet article peut intéresser, n’hésitez pas à le liker et à le partager sur les réseaux sociaux. Si vous souhaitez me faire part de vos remarques, de vos problèmes, de vos suggestions de prochain article ou tout simplement de votre soutien n’hésitez pas à m’en faire part dans les commentaires, je vous en remercie.

Crédit photo : Blickpixel, openClipart-vector

Partager l'article :
  •  
  •  
  •  
  •  

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *