Si piensas en las apps que usas con más frecuencia en el teléfono, casi todas las tienen al menos una lista. La pantalla del historial de llamadas, la app de Contactos, tu app de redes sociales favorita… Todas muestran una lista de datos. Como se ve en la siguiente captura de pantalla, en algunas de estas apps se muestra una lista simple de palabras o frases, mientras que otras muestran elementos más complejos, como tarjetas con texto e imágenes. Independientemente de cuál sea el contenido, mostrar una lista de datos es una de las tareas de IU más comunes de Android.
Para ayudarte a crear apps con listas, Android proporciona RecyclerView
. RecyclerView
está diseñado para ser muy eficiente, incluso con listas grandes, ya que puede reutilizar o reciclar las vistas que se desplazan fuera de la pantalla. Cuando se desplaza un elemento de la lista fuera de la pantalla, RecyclerView
reutiliza la vista del siguiente elemento que se mostrará. Esto significa que el elemento se rellena con contenido nuevo que se desplaza hacia la pantalla. Este comportamiento de RecyclerView
ahorra mucho tiempo de procesamiento y ayuda a las listas a desplazarse de forma más fluida.
En la secuencia que se muestra a continuación, puedes ver que se completó una vista con datos, ABC
. Después de que la vista se desplace fuera de la pantalla, RecyclerView
volverá a usar la vista para datos nuevos, XYZ
.
En este codelab, compilarás la app de Affirmations. Es una app sencilla que muestra diez afirmaciones positivas como texto en una lista de desplazamiento. Luego, en el codelab de seguimiento, irás un paso más allá; agregarás una imagen inspiradora a cada afirmación y perfeccionarás la IU de la app.
Requisitos previos
- Crear un proyecto a partir de una plantilla en Android Studio
- Agregar recursos de strings a una app
- Definir un diseño en XML
- Comprender las clases y la herencia en Kotlin (incluidas las clases abstractas)
- Heredar de una clase existente y anular sus métodos
- Usa la documentación disponible en developer.android.com para consultar las clases que proporciona el framework de Android.
Qué aprenderás
- Cómo usar un
RecyclerView
para mostrar una lista de datos - Cómo organizar el código en paquetes
- Cómo usar adaptadores con
RecyclerView
para personalizar el aspecto de un elemento de lista individual
Qué compilarás
- Una app que muestra una lista de strings de afirmación con un
RecyclerView
Requisitos
- Una computadora que tenga instalado Android Studio 4.1 o una versión posterior
Cómo crear un proyecto de actividad vacía
Antes de crear un proyecto nuevo, asegúrate de utilizar Android Studio 4.1 o versiones posteriores.
- Inicia un nuevo proyecto de Kotlin en Android Studio con la plantilla Empty Activity.
- Ingresa Affirmations como Name de la app, com.example.affirmations como el Package name y elige API Level 19 como Minimum SDK.
- Haz clic en Finish para crear el proyecto.
El siguiente paso para crear la app de Affirmations es agregar recursos. Agregarás lo siguiente al proyecto:
- Recursos de strings para mostrar como afirmaciones en la app
- Una fuente de datos para proporcionar una lista de afirmaciones a la app
Cómo agregar strings de Affirmation
- En la ventana Project, abre app > res > values > strings.xml. Este archivo actualmente tiene un único recurso que es el nombre de la app.
- En
strings.xml
, agrega las siguientes afirmaciones como recursos de strings individuales. Asigna el nombreaffirmation1
,affirmation2
, y así sucesivamente.
Texto de 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.
Cuando termines, el archivo strings.xml
debería verse de la siguiente manera.
<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>
Ahora que agregaste recursos de strings, puedes hacer referencia a ellos en tu código como R.string.affirmation1
o R.string.affirmation2
.
Cómo crear un nuevo paquete
Organiza tu código de forma lógica para que tú y otros desarrolladores lo comprendan, mantengan y extiendan. De la misma manera en que puedes organizar los papeles en archivos y carpetas, puedes organizar tu código en archivos y paquetes.
¿Qué es un paquete?
- En Android Studio, en la ventana Project (Android), revisa los archivos de tu nuevo proyecto en app > java para la app de Affirmations. Deberían ser similares a la captura de pantalla que se muestra a continuación, donde aparecen tres paquetes, uno para tu código (com.example.affirmations) y dos para los archivos de prueba (com.example.affirmations (androidTest) y com.example.affirmations (test)).
- Observa que el nombre del paquete consta de varias palabras separadas por un punto.
Existen dos formas de usar los paquetes.
- Crea diferentes paquetes para diferentes partes de tu código. Por ejemplo, los desarrolladores a menudo separan las clases que trabajan con los datos y las clases que compilan la IU en diferentes paquetes.
- Usa código de otros paquetes en tu código. Para usar las clases de otros paquetes, debes definirlas en las dependencias de tu sistema de compilación. También se recomienda usar
import
para importarlas a tu código a fin de que puedas usar los nombres cortos (p. ej.,TextView
) en lugar de los nombres completamente calificados (p. ej.,android.widget.TextView
). Por ejemplo, ya usaste declaracionesimport
para clases comosqrt
(import kotlin.math.sqrt
) yView
(import android.view.View
).
En la app de Affirmations, además de importar clases de Android y Kotlin, organizarás la app en varios paquetes. Incluso cuando no tengas muchas clases para tu app, te recomendamos usar paquetes a fin de agrupar clases por funcionalidad.
Cómo nombrar paquetes
Puedes asignar cualquier nombre, siempre que sea único a nivel global. Ningún otro paquete publicado en cualquier lugar puede tener el mismo nombre. Debido a que hay una gran cantidad de paquetes, y cuando hay nombres únicos aleatorios es difícil, los programadores usan convenciones para facilitar la creación y la comprensión de los nombres de paquetes.
- El nombre del paquete suele estar estructurado de forma general a específica, y cada parte del nombre está en letras minúsculas y separada por un punto. Importante: El punto es solo una parte del nombre. No indica una jerarquía en el código ni la estructura de una carpeta.
- Debido a que los dominios de Internet son únicos a nivel global, es una convención que utilices un dominio, en general, el tuyo o el de tu empresa, como primera parte del nombre.
- Puedes elegir los nombres de los paquetes para indicar qué hay dentro y cómo se relacionan entre sí.
- Para ejemplos de código como este, se suele usar
com.example
seguido del nombre de la app.
A continuación, se presentan algunos ejemplos de nombres de paquetes predefinidos y su contenido:
kotlin.math
: Funciones y constantes matemáticas.android.widget
: Vistas, comoTextView
.
Cómo crear un paquete
- En Android Studio, en el panel Project, haz clic con el botón derecho en app > java > com.example.affirmations y selecciona New > Package.
- En la ventana emergente New Package, observa el prefijo del nombre del paquete sugerido. La primera parte sugerida es el nombre del paquete en el que hiciste clic con el botón derecho. Aunque los nombres de los paquetes no crean una jerarquía de paquetes, la reutilización de partes del nombre se usa para indicar una relación y una organización del contenido.
- En la ventana emergente, agrega model al final del nombre del paquete sugerido. A menudo, los desarrolladores usan model como el nombre del paquete para las clases que modelan (o representan) los datos.
- Presiona Intro. Se creará un paquete nuevo en el paquete com.example.affirmations (raíz). Este nuevo paquete contendrá cualquier clase relacionada con los datos definida en tu app.
Cómo crear la clase de datos de Affirmation
En esta tarea, crearás una clase llamada Affirmation.
Una instancia de objeto Affirmation
representa una afirmación y contiene el ID de recurso de la string con la afirmación.
- Haz clic con el botón derecho en el paquete com.example.affirmations.model y selecciona New > Kotlin File/Class.
- En la ventana emergente, selecciona Class y, luego, ingresa
Affirmation
como el nombre de la clase. Esto crea un archivo nuevo llamadoAffirmation.kt
en el paquetemodel
. - Para hacer que
Affirmation
sea una clase de datos, agrega la palabra clavedata
antes de la definición de la clase. Esto deja un error, ya que las clases de datos deben tener al menos una propiedad definida.
Affirmation.kt
package com.example.affirmations.model
data class Affirmation {
}
Cuando creas una instancia de Affirmation
, debes pasar el ID de recurso para la string de afirmación. El ID del recurso es un número entero.
- Agrega un parámetro de número entero
val
stringResourceId
al constructor de la claseAffirmation
. Esto elimina tu error.
package com.example.affirmations.model
data class Affirmation(val stringResourceId: Int)
Cómo crear una clase para que sea una fuente de datos
Los datos que se muestran en la app pueden provenir de distintas fuentes (p. ej., dentro del proyecto de tu app o de una fuente externa que requiere conexión a Internet para descargar datos). Como resultado, es posible que los datos no estén en el formato exacto que necesitas. El resto de la app no debería tener que preocuparse por la ubicación de los datos o el formato original. Puedes y debes ocultar esta preparación de datos en una clase Datasource
separada que prepara los datos para la app.
Como la preparación de datos es una preocupación diferente, coloca la clase Datasource
en un paquete data independiente.
- En Android Studio, en la ventana Project, haz clic con el botón derecho en app > java > com.example.affirmations y selecciona New > Package.
- Ingresa
data
como la última parte del nombre del paquete. - Haz clic con el botón derecho en el paquete
data
y selecciona New File/Class. - Ingresa
Datasource
como el nombre de la clase. - Dentro de la clase
Datasource
, crea una función llamadaloadAffirmations()
.
La función loadAffirmations()
debe mostrar una lista de Affirmations
. Para ello, crea una lista y complétala con una instancia Affirmation
para cada string de recurso.
- Declara
List<Affirmation>
como el tipo de datos que se muestra del métodoloadAffirmations()
. - En el cuerpo de
loadAffirmations()
, agrega una sentenciareturn
. - Después de la palabra clave
return
, llama alistOf<>()
para crear unaList
. - Dentro de los paréntesis angulares
<>
, especifica el tipo de elementos de lista comoAffirmation
. Si es necesario, importacom.example.affirmations.model.Affirmation
. - Dentro de los paréntesis, crea una
Affirmation
y pasaR.string.affirmation1
como el ID de recurso, como se muestra a continuación.
Affirmation(R.string.affirmation1)
- Agrega los objetos
Affirmation
restantes a la lista de todas las afirmaciones, separados por comas. El código finalizado debería verse como el siguiente.
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)
)
}
}
[Opcional] Muestra el tamaño de la lista de Affirmations en un TextView
Para verificar que puedes crear una lista de afirmaciones, puedes llamar a loadAffirmations()
y mostrar el tamaño de la lista de afirmaciones que aparecen en el TextView
incluido con tu plantilla de app Empty Activity.
- En
layouts/activity_main.xml
, asigna alTextView
incluido con la plantilla unid
detextview
. - En
MainActivity
, en el métodoonCreate()
después del código existente, obtén una referencia atextview
.
val textView: TextView = findViewById(R.id.textview)
- Después, agrega el código para crear y mostrar el tamaño de la lista de afirmaciones. Crea un
Datasource
, llama aloadAffirmations()
, obtén el tamaño de la lista que se muestra, conviértelo en una string y asígnalo comotext
detextView
.
textView.text = Datasource().loadAffirmations().size.toString()
- Ejecuta la app. La pantalla debería verse como se muestra a continuación.
- Borra el código que acabas de agregar en
MainActivity
.
En esta tarea, configurarás un RecyclerView
para mostrar la lista de Affirmations
.
Hay varias piezas involucradas en la creación y el uso de RecyclerView
. Imagina que son una división del trabajo. En el siguiente diagrama, se muestra una descripción general, y obtendrás más información sobre cada pieza a medida que la implementes.
- item: Un elemento de datos de la lista para mostrar. Representa un objeto
Affirmation
en tu app. - Adapter: Toma datos y los prepara para que
RecyclerView
los muestre. - ViewHolders: Un conjunto de vistas para que
RecyclerView
lo use y reutilice a fin de mostrar afirmaciones. - RecyclerView: Vistas en la pantalla.
Cómo agregar un RecyclerView al diseño
La app de Affirmations consta de una sola actividad llamada MainActivity
, y su archivo de diseño se llama activity_main
.xml
. Primero, debes agregar el elemento RecyclerView
al diseño de MainActivity
.
- Abre
activity_main.xml
(app > res > layout > activity_main.xml) - Si todavía no la utilizas, cambia a la opción Split view.
- Borra el
TextView
.
El diseño actual usa ConstraintLayout
. ConstraintLayout
es ideal y flexible cuando quieres posicionar varias vistas secundarias en un diseño. Como tu diseño solo tiene una sola vista secundaria, RecyclerView
, puedes cambiar a un ViewGroup
más simple llamado FrameLayout
, que se debe usar para mantener una sola vista secundaria.
- En el archivo XML, reemplaza
ConstraintLayout
porFrameLayout
. El diseño completado debe verse como se muestra a continuación.
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>
- Cambia a la vista Design.
- En Palette, selecciona Containers y busca RecyclerView.
- Arrastra una RecyclerView al diseño.
- Si aparece, lee la ventana emergente Add Project Dependency y haz clic en OK. (Si no aparece la ventana emergente, no se requiere ninguna acción).
- Espera a que Android Studio termine y que
RecyclerView
aparezca en tu diseño. - Si es necesario, cambia los atributos
layout_width
ylayout_height
deRecyclerView
amatch_parent
para queRecyclerView
pueda llenar toda la pantalla. - Configura el ID del recurso de
RecyclerView
comorecycler_view
.
RecyclerView
admite mostrar elementos de diferentes formas, como una lista lineal o una cuadrícula. La organización de los elementos se controla mediante un LayoutManager
. El framework de Android proporciona administradores de diseño para diseños básicos de elementos. La app de Affirmations muestra elementos como una lista vertical, por lo que puedes usar LinearLayoutManager
.
- Vuelve a la vista Code. En el código XML, dentro del elemento
RecyclerView
, agregaLinearLayoutManager
como atributo del administrador de diseño deRecyclerView
, como se muestra a continuación.
app:layoutManager="LinearLayoutManager"
Para poder desplazarte por una lista vertical de elementos más extensa que la pantalla, debes agregar una barra de desplazamiento vertical.
- En
RecyclerView
, agrega un atributoandroid:scrollbars
configurado comovertical
.
android:scrollbars="vertical"
El diseño XML final debería verse de la siguiente manera:
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>
- Ejecuta tu app.
El proyecto debería compilarse y ejecutarse sin problemas. Sin embargo, solo se muestra un fondo blanco en la app porque falta un fragmento de código crucial. Ahora, tienes la fuente de los datos y RecyclerView
agregados a tu diseño, pero RecyclerView
no tiene información sobre cómo mostrar los objetos Affirmation
.
Cómo implementar un Adapter para RecyclerView
Tu app necesita una forma de tomar los datos de Datasource
y formatearlos para que se pueda mostrar cada Affirmation
como un elemento en RecyclerView
.
Adapter es un patrón de diseño que adapta los datos a algo que puede usar RecyclerView
. En este caso, necesitas un adaptador que tome una instancia de Affirmation
de la lista que muestra loadAffirmations()
y la convierta en una vista de elementos de lista para que pueda aparecer en RecyclerView.
.
Cuando ejecutas la app, RecyclerView
usa el adaptador para descifrar cómo mostrar tus datos en la pantalla. RecyclerView
le pide al adaptador que cree una nueva vista de elementos de lista para el primer elemento de datos de tu lista. Una vez que tenga la vista, le solicitará al adaptador que proporcione los datos para dibujar el elemento. Este proceso se repite hasta que RecyclerView
no necesita más vistas para cubrir la pantalla. Si solo 3 vistas de elementos de la lista se ajustan a la pantalla a la vez, RecyclerView
solo solicita al adaptador que prepare esas 3 vistas de elementos de lista (en lugar de las 10 vistas de elementos de lista).
En este paso, compilarás un adaptador que adaptará una instancia del objeto Affirmation
para que pueda mostrarse en RecyclerView
.
Cómo crear el Adapter
Un adaptador tiene varias partes, y escribirás bastante código más complejo que lo que hiciste en este curso hasta ahora. No hay problema si al principio no comprendes todos los detalles. Después de completar toda la app con un RecyclerView
, podrás comprender mejor cómo encajan todas las partes. También podrás volver a usar este código como base para las futuras apps que crees con un RecyclerView
.
Cómo crear un diseño para elementos
Cada elemento de RecyclerView
tiene su propio diseño, que se define en un archivo de diseño separado. Dado que solo se mostrará una string, puedes usar TextView
para el diseño de tu elemento.
- En res > layout, crea un nuevo File vacío llamado
list_item.xml
. - Abre
list_item.xml
en la vista Code. - Agrega un
TextView
conid
item_title
. - Agrega
wrap_content
paralayout_width
ylayout_height
, como se muestra en el siguiente código.
Ten en cuenta que no necesitas un ViewGroup
alrededor de tu diseño, ya que este diseño de elemento de lista se aumentará y se agregará como elemento secundario del elemento superior RecyclerView
.
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" />
También podrías haber usado File > New > Layout Resource File, con File name list_item.xml
y TextView
como Root element. Luego, actualiza el código generado para que coincida con el código anterior.
Cómo crear una clase ItemAdapter
- En Android Studio, en el panel Project, haz clic con el botón derecho en app > java > com.example.affirmations y selecciona New > Package.
- Ingresa
adapter
como la última parte del nombre del paquete. - Haz clic con el botón derecho en el paquete
adapter
y selecciona New > Kotlin File/Class. - Ingresa
ItemAdapter
como el nombre de la clase y termina. Se abre el archivoItemAdapter.kt
.
Debes agregar un parámetro al constructor de ItemAdapter
para poder pasar la lista de afirmaciones al adaptador.
- Agrega un parámetro al constructor
ItemAdapter
, que es unval
llamadodataset
de tipoList<Affirmation>
. ImportaAffirmation
si es necesario. - Como el objeto
dataset
solo se usará dentro de esta clase, hazloprivate
.
ItemAdapter.kt
import com.example.affirmations.model.Affirmation
class ItemAdapter(private val dataset: List<Affirmation>) {
}
ItemAdapter
necesita información sobre cómo resolver los recursos de strings. Esta y otra información sobre la app se almacena en una instancia del objeto Context
, que puedes pasar a una instancia ItemAdapter
.
- Agrega un parámetro al constructor
ItemAdapter
, que es unval
llamadocontext
de tipoContext
. Ubícalo como el primer parámetro en el constructor.
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {
}
Cómo crear un ViewHolder
RecyclerView
no interactúa directamente con las vistas de elementos, sino con ViewHolders
. Un objeto ViewHolder
representa una sola vista de elementos de lista en RecyclerView
y se puede volver a usar cuando sea posible. Una instancia ViewHolder
contiene referencias a las vistas individuales dentro de un diseño de elemento de lista (por eso se denomina, "view holder", "contenedor de vistas"). Esto facilita la actualización de la vista de elemento de lista con datos nuevos. Los contenedores de vistas también agregan información que RecyclerView
usa para mover las vistas de manera eficiente por la pantalla.
- Dentro de la clase
ItemAdapter
, antes de la llave de cierre paraItemAdapter
, crea una claseItemViewHolder
.
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {
class ItemViewHolder()
}
- Definir una clase dentro de otra clase se llama crear una clase anidada.
- Como
ItemAdapter
solo usaItemViewHolder
, si se crea dentro deItemAdapter
, se muestra esta relación. Esto no es obligatorio, pero ayuda a otros desarrolladores a comprender la estructura de tu programa.
- Agrega un
private
val
view
de tipoView
como parámetro al constructor de la claseItemViewHolder
. - Haz que
ItemViewHolder
sea una subclase deRecyclerView
.ViewHolder
y pasa el parámetroview
al constructor de la superclase. - Dentro de
ItemViewHolder
, define una propiedadval
detextView
que sea del tipoTextView
. Asigna la vista con el IDitem_title
que definiste enlist_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)
}
}
Cómo anular métodos de adaptador
- Agrega el código para extender tu
ItemAdapter
de la clase abstractaRecyclerView.Adapter
. EspecificaItemAdapter.ItemViewHolder
como el tipo de contenedor de vistas entre paréntesis angulares.
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)
}
}
Verás un error porque debes implementar algunos métodos abstractos de RecyclerView.Adapter
.
- Coloca el cursor en
ItemAdapter
y presiona Command + I (Control + I en Windows). Esto te muestra la lista de métodos que debes implementar:getItemCount()
,onCreateViewHolder()
yonBindViewHolder()
.
- Selecciona las tres funciones con Mayúsculas + clic y haz clic en OK.
Esto crea stubs con los parámetros correctos para los tres métodos, como se muestra a continuación.
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")
}
No deberías ver más errores. A continuación, debes implementar esos métodos a fin de que realicen las acciones correctas para tu app.
Cómo implementar getItemCount()
El método getItemCount()
debe mostrar el tamaño de tu conjunto de datos. Los datos de tu app se encuentran en la propiedad dataset
que pasas al constructor ItemAdapter
, y puedes obtener su tamaño con size
.
- Reemplaza
getItemCount()
por lo siguiente:
override fun getItemCount() = dataset.size
Es una forma más concisa de escribir lo siguiente:
override fun getItemCount(): Int {
return dataset.size
}
Cómo implementar onCreateViewHolder()
El administrador de diseño llama al método onCreateViewHolder()
a fin de crear nuevas interfaces de vista para RecyclerView
(cuando no hay contenedores de vistas existentes que puedan reutilizarse). Recuerda que un contenedor de vistas representa una sola vista de elementos de lista.
El método onCreateViewHolder()
toma dos parámetros y muestra un ViewHolder
nuevo.
- Un parámetro
parent
, que es el grupo de vistas al que se adjuntará la nueva vista de elemento de la lista como elemento secundario. El superior es elRecyclerView
. - Un parámetro
viewType
, que se vuelve importante cuando hay varios tipos de vistas de elementos en el mismoRecyclerView
. Si tienes diferentes diseños de elementos de lista enRecyclerView
, existen diferentes tipos de vistas de elementos. Solo puedes reciclar vistas con el mismo tipo de vista de elemento. En tu caso, solo hay un diseño de elemento de lista y un tipo de vista de elemento, por lo que no tienes que preocuparte por este parámetro.
- En el método
onCreateViewHolder()
, obtén una instancia deLayoutInflater
a partir del contexto proporcionado (context
deparent
). El amplificador de diseño sabe cómo aumentar un diseño XML a una jerarquía de objetos de vista.
val adapterLayout = LayoutInflater.from(parent.context)
- Cuando tengas una instancia de objeto
LayoutInflater
, agrega un punto seguido de otra llamada de método para aumentar la vista real de elementos de lista. Pasa el ID de recurso de diseño XMLR.layout.list_item
y el grupo de vistasparent
. El tercer argumento booleano esattachToRoot
. Este argumento debe serfalse
, porqueRecyclerView
agrega este elemento a la jerarquía de vistas cuando sea el momento.
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
Ahora adapterLayout
tiene una referencia a la vista de elementos de lista (donde más adelante podemos encontrar
vistas secundarias como TextView
).
- En
onCreateViewHolder()
, muestra una nueva instancia deItemViewHolder
en la que la vista raíz esadapterLayout
.
return ItemViewHolder(adapterLayout)
Este es el código para onCreateViewHolder()
hasta ahora.
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)
}
Cómo implementar onBindViewHolder()
El último método que debes anular es onBindViewHolder()
. El administrador de diseño llama a este método para reemplazar el contenido de una vista de elementos de lista.
El método onBindViewHolder()
tiene dos parámetros: ItemViewHolder
, creado anteriormente por el método onCreateViewHolder()
, y un int
que representa el elemento actual position
en la lista. En este método, encontrarás el objeto Affirmation
adecuado del conjunto de datos según la posición.
- Dentro de
onBindViewHolder()
, crea unval
item
y obtén el elemento en laposition
determinada en eldataset
.
val item = dataset[position]
Por último, debes actualizar todas las vistas a las que hace referencia el contenedor de vistas para reflejar los datos correctos de este elemento. En este caso, solo hay una vista: TextView
dentro de ItemViewHolder
. Configura el texto de TextView
para mostrar la string Affirmation
de este elemento.
- Con una instancia de objeto
Affirmation
, puedes buscar el ID de recurso de strings correspondiente llamando aitem.stringResourceId
. Sin embargo, es un número entero y necesitas encontrar la asignación al valor de string real.
En el framework de Android, puedes llamar a getString()
con un ID de recurso de strings, y este mostrará el valor de string asociado. getString()
es un método en la clase Resources
, y puedes obtener una instancia de la clase Resources
a través de context
.
Esto significa que puedes llamar a context.resources.getString()
y pasar un ID de recurso de strings. La string resultante se puede establecer como text
de textView
en holder
ItemViewHolder
. En resumen, esta línea de código actualiza el contenedor de vistas para mostrar la string de afirmación.
holder.textView.text = context.resources.getString(item.stringResourceId)
El método onBindViewHolder()
completo debería verse de la siguiente manera:
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = dataset[position]
holder.textView.text = context.resources.getString(item.stringResourceId)
}
Aquí está el código de adaptador terminado.
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
}
Ahora que implementaste el ItemAdapter
, debes indicarle a RecyclerView
que use este adaptador.
Cómo modificar MainActivity para usar un RecyclerView
Para finalizar, debes usar tus clases Datasource
y ItemAdapter
a fin de crear y mostrar elementos en RecyclerView
. Puedes hacerlo en MainActivity
.
- Abre
MainActivity.kt
. - En
MainActivity,
, ve al métodoonCreate()
. Inserta el nuevo código descrito en los siguientes pasos después de la llamada asetContentView(R.layout.activity_main).
. - Crea una instancia de
Datasource
y llama al métodoloadAffirmations()
. Almacena la lista de afirmaciones que se muestra en unaval
llamadamyDataset
.
val myDataset = Datasource().loadAffirmations()
- Crea una variable llamada
recyclerView
y usafindViewById()
para buscar una referencia alRecyclerView
dentro del diseño.
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
- Para indicarle a
recyclerView
que use la claseItemAdapter
que creaste, crea una instanciaItemAdapter
nueva.ItemAdapter
espera dos parámetros: el contexto (this
) de esta actividad y las afirmaciones enmyDataset
. - Asigna el objeto
ItemAdapter
a la propiedadadapter
derecyclerView
.
recyclerView.adapter = ItemAdapter(this, myDataset)
- Como el tamaño del diseño de tu
RecyclerView
es fijo en el diseño de la actividad, puedes establecer el parámetrosetHasFixedSize
deRecyclerView
entrue
. Esta configuración solo es necesaria para mejorar el rendimiento. Úsala si sabes que los cambios en el contenido no cambiarán el tamaño del diseño deRecyclerView
.
recyclerView.setHasFixedSize(true)
- Cuando termines, el código para
MainActivity
debe ser similar al siguiente.
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)
}
}
- Ejecuta la app. Deberías ver una lista de strings de afirmación en la pantalla.
¡Felicitaciones! Acabas de crear una app que muestra una lista de datos con RecyclerView
y un adaptador personalizado. Dedica un tiempo a analizar el código que creaste y comprender cómo funcionan en conjunto las distintas piezas.
Esta app cuenta con todas las piezas necesarias para mostrar tus afirmaciones, pero aún no está lista para la etapa de producción. Se podría mejorar la IU. En el siguiente codelab, mejorarás tu código, aprenderás a agregar imágenes a la app y perfeccionarás la IU.
El código de la solución para este codelab se encuentra en el proyecto y módulo que se muestran a continuación. Ten en cuenta que algunos de los archivos Kotlin están en paquetes diferentes, como lo indica la sentencia package
al comienzo del archivo.
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" />
- El widget de
RecyclerView
te ayuda a mostrar una lista de datos. RecyclerView
usa el patrón del adaptador para adaptar y mostrar los datos.ViewHolder
crea y contiene las vistas paraRecyclerView
.RecyclerView
incluyeLayoutManagers
.RecyclerView
delega la distribución de los elementos aLayoutManagers
.
Para implementar el adaptador, sigue estos pasos:
- Crea una nueva clase para el adaptador, por ejemplo,
ItemAdapter
. - Crea una clase
ViewHolder
personalizada que represente una sola vista de elementos de lista. Realiza la extensión desde la claseRecyclerView.ViewHolder
. - Modifica la clase
ItemAdapter
para que se extienda desde la claseRecyclerView
.Adapter
con la clase personalizadaViewHolder
. - Implementa estos métodos dentro del adaptador:
getItemsCount()
,onCreateViewHolder()
yonBindViewHolder()
.