RecyclerView amb llista en memòria (Kotlin)¶
Documentació oficial: https://developer.android.com/develop/ui/views/layout/recyclerview
Exemple: https://github.com/davidfs-itic/RecyclerView
1. Què és un RecyclerView?¶
Un RecyclerView és un component que permet mostrar llistes (o graelles) d’elements reciclant les vistes per millorar el rendiment. Substitueix l’antic ListView i ofereix més flexibilitat (layout managers, animacions, etc.).
Per utilitzar un RecyclerView cal:
- Una llista de dades (en memòria, en aquest exemple).
- Un layout XML per a la pantalla que conté el RecyclerView.
- Un layout XML per a cada fila (ítem) de la llista.
- Un Adapter amb un ViewHolder per “pintar” les dades a cada fila.
2. Model de dades (data class i llista)¶
Data Class¶
Creem una classe senzilla per representar cada element de la llista.
1 2 3 4 5 | |
Llista de dades en un object (Kotlin)¶
Què és un object a Kotlin?¶
En Kotlin, un object és una construcció que crea una única instància (patró Singleton) de manera automàtica. S’utilitza quan es vol un únic punt d’accés compartit, per exemple una llista de dades comuna per a diferents pantalles.
Característiques principals:¶
- No cal fer new ni cridar cap constructor; s’accedeix pel nom (DataSource.items).
- Es crea la instància la primera vegada que es fa servir.
- És útil per guardar dades en memòria mentre l’app està en execució.
Objecte que conté la llista: DataSource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 | |
1 | |
L’objecte DataSource actua com un “mini repositori de dades” senzill, sense base de dades ni API.
(Per a veure com s'ha de crear un repositori de dades, veure l'apartat repositori a Arquitectura)
3. Layout de cada element (item_row.xml)¶
Aquest layout defineix com es veu una fila de la llista.
<!-- Fitxer: res/layout/item_row.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Títol"
android:textStyle="bold"
android:textSize="18sp" />
<TextView
android:id="@+id/tvSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Subtítol"
android:textSize="14sp" />
</LinearLayout>
4. Layout amb el RecyclerView (activity_main.xml)¶
Aquest layout conté el RecyclerView que ocuparà tota la pantalla.
<!-- Fitxer: res/layout/activity_main.xml -->
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
5. Adapter i ViewHolder (MyAdapter.kt)¶
En un RecyclerView aquestes classes sempre treballen juntes:
El Adapter sap:¶
- Quantes files hi ha.
- Quin layout s’utilitza per a cada fila.
- Quines dades s’han de mostrar a cada posició.
El ViewHolder sap:¶
- Quines vistes (TextView, ImageView, etc.) té una fila.
- On escriure les dades quan l’Adapter li digui “pinta l’element X”.
Sense Adapter i ViewHolder, el RecyclerView no sap ni quantes files mostrar, ni com dibuixar-les.
Exemple codi Holder¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
Explicació:¶
Classe
class MyViewHolder(... ) : RecyclerView.ViewHolder(itemView)
Propietats privades (tvTitle, tvSubtitle):
El ViewHolder busca una sola vega*da les vistes del layout item_row.xml i s’hi queda la referència.
Mètode bind(item: MyItem):
És el punt únic per: - Escriure dades a les vistes. - Configurar listeners específics d’aquesta fila (clic, long‑click…).
Això treu responsabilitat de “pintar dades” de l’Adapter i la concentra al ViewHolder.
Exemple codi Adapter¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
Explicació:¶
L’Adapter ja no sap com s’actualitzen exactament les vistes; delega aquesta feina a holder.bind(item).
Responsabilitats clares de cada mètode (tal com recomana la documentació oficial):
- onCreateViewHolder: infla el layout i crea el ViewHolder.
- getItemCount: diu al RecyclerView quantes files han de existir.
- onBindViewHolder: passa el model (MyItem) al ViewHolder perquè l’actualitzi.
6. Activity¶
Exemple de codi en l'activity. Els comentaris expliquen els passos que s'han de seguir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
Tot i que s'utilitza una funció (anomenada callback) per passar la lògica de gestió del click en un ítem (recodem que aixó ho gestiona el ViewHolder), no és una pràctica recomenada. Està aquí per simplicitat.
Veieu "Exemple inversió de dependències amb RecyclerView" a l'apartat Arquitectura
7. Resum¶
Idea clau:¶
En un RecyclerView, el ViewHolder encapsula la vista d’una fila, en guarda les referències a les seves sub‑vistes i pot contenir la lògica específica d’aquesta fila (com ara actualitzar les dades o gestionar clics), mentre que l’Adapter crea aquests ViewHolder, els recicla i els alimenta amb les dades correctes de la llista.
- onCreateViewHolder crea la vista i el ViewHolder.
- onBindViewHolder omple aquesta vista amb les dades de la posició concreta.
Frase “resum” que podeu recordar:¶
L’Adapter:
- Crea ViewHolder nous (onCreateViewHolder).
- Diu quantes files hi ha (getItemCount).
- Omple les vistes del ViewHolder amb dades (onBindViewHolder).
El ViewHolder - Guarda referències a les vistes de la fila (TextView, ImageView, etc.). - Encapsula la lògica de “bind”: té una funció bind(item: MyItem) que rep el model i actualitza totes les vistes. - Gestiona esdeveniments de la fila: per exemple, configurar setOnClickListener sobre itemView o sobre algun botó concret, i notifica l’Adapter o un listener extern.