Listas de elementos en Android con RecyclerView: Tutorial a fondo
Antonio Leiva

En Android existen varios componentes diferentes para crear listas, pero si hablamos del más recomendado y flexible, entonces nos tenemos que quedar con RecyclerView.

RecyclerView es una librería de Jetpack que nos permite crear listas con los formatos que necesitemos de forma muy flexible, ya que consta de componentes diferentes. Cada uno de ellos se encarga de una tarea muy específica.

Además, para evitar problemas de rendimiento, es capaz de reciclar las vistas que conforman las celdas que se muestran en la pantalla, de tal forma que no se necesitan muchas más celdas de las que están visibles. Cuando una sale de pantalla, se reutiliza para otro elemento que entra.

Cómo usar RecyclerView

En el vídeo de hoy te enseño cómo utilizar el RecyclerView paso a paso:

Montar RecyclerView desde cero

Lo primero que vamos a necesitar es importar la dependencia de RecyclerView para ello dentro de nuestro proyecto vamos a la carpeta Gradle Scripts > build.grandle (Module: Android_Desde_Cero.app), ya dentro del archivo en donde vayamos hacer la importación le daremos a las teclas Alt + Enter y nos desplegara la opción de agregar las librería de soporte add library dependecy

implementation 'androidx.recyclerview:recyclerview:1.2.1'

Para usar el RecyclerView nos vamos a ir al activity_main y eliminaremos nuestros elemento movie de nuestro LinearLayout. Después convertimos nuestro LinearLayout en FrameLayout y agregaremos nuestro RecliclerView

Además en nuestras propiedades del FrameLayout le pondremos en el layout_width que sea mattch_parent para que ocupe toda la pantalla

Pero este componente por si solo no es capaz de hacer nada, necesita de otros sub-componentes que le va a ir ayudando hacer determinadas tareas dentro del proceso de creación de esas vistas que va a formar parte del ReciclerView, lo que vamos hacer es utilizar el LinearLayout Manager que lo que va hacer es posicionar los elementos uno debajo de otro, para ello nos vamos al ReciclerView y en el XML le asignamos la propiedad layoutManager con el valor de:

androidx.recyclerview.widget.LinearLayoutManager

Otra cosa que también podemos definir a nivel de diseño y que solo lo podremos ver en el diseñador es seleccionar cual va hacer el listitem que seria el elemento de la lista que se va a utilizar para crear el RecyclerView

hacemos click al final en los recursos y nos mostrara los elementos, seleccionaremos nuestra view_movie.

Compilaremos nuestro proyecto en caso de no haberlo hecho al momento de agregar nuestra dependencia. Continuamente tenemos que comentar de momento en nuestro MainActivity las ultimas dos lineas porque no las estamos usando y nos da error ya que hemos cambiado el layout que estamos utilizando en el activity_main.

Nos iremos al view_movie, antes lo estábamos usando en una vista personalizada pero para el resto del proyecto no lo vamos a usar, esto solo era para explicar la parte sobre vistas personalizadas. Así que vamos a modificar el XML de esta vista para que se adapte un poco a lo que necesitamos, para ello sustituiremos nuestra etiqueta Merge por LinearLayout que era el que realmente nos daba la estructura que necesitábamos

aprovecharemos para cambiar el layout_height de nuestro LinearLayout a un valor de wrap_content para que ocupe nuestro contenedor nada más. Si nos vamos al activity_main veremos el listado de elementos.

y por ultimo vamos a necesitar un Adapter es uno de los puntos principales dentro del uso de los ReciclerView porque si pensamos en la ventaja principal que tienen ReciclerView es que nos va a permitir crear listas infinitas pero que esto no suponga una penalización en rendimiento para los dispositivos pero los dispositivos tienen al final recursos limitados y si cargáramos todas las vistas existentes para un listado muy largo dentro del RecyclerView eso supondría una penalización muy grande en rendimiento porque se tienen que crear un montón de objetos por detrás, cargar un montón de imágenes y eso no tiene sentido a nivel de rendimiento.

¿Por qué se llama RecyclerView? Porque lo que haces es reciclar las vistas que utiliza para mostrar los elementos en la pantalla. Cuando un elemento sale de la pantalla esa vista se puede reutilizar para los nuevos elementos que va a ir entrando por debajo, de tal forma que ese rendimiento es muchísimo mayor porque solo necesitamos un numero de elementos que no es normalmente mayor a todos los elementos que se pueden ver dentro de la pantalla en un momento determinado.

El Adapter lo va a utilizar el ReciclerView por un lado para crear vistas nuevas cuando las necesite y por otra para asignar a una vista el valor que corresponda. La parte de creación solo se va a llamar cuando se necesite una vista nueva, mientras que la de actualización de esas vistas se va a llamar cada vez que entre una nueva celda independientemente de si se ha creado desde cero o tenemos que actualizar los datos porque se esta reciclando esa vista para mostrar un elemento nuevo.

¿Cómo se configura ese Adapter? Si nos vamos a la MainActivity ahora si en vez de recuperar la movie vamos a recuperar el RecyclerView, para eso le daremos un identificar con el valor recycler

Y luego regresamos a nuestro MainActivit donde recuperaremos nuestro RecyclerView,

val recycler = findViewById<RecyclerView>(R.id.recycler)

despues le asignaremos un Adapter

recycler.adapter = 

y aquí nos vamos a crear nuestro propio Adapter, así que nos vamos a crear una nueva clase de Kotlin que la vamos a llamar MoviesAdapter , ¿Cómo definimos el Adapter? para ello necesitamos extender del RecyclerView.Adapter

Si nos fijamos este Adapter esta esperando un tipo genérico que extienda de ViewHolder es un nuevo elemento dentro del RecyclerView que lo que nos permite es almacenar una vista dentro de el y luego gestiona de cierta manera las partes del RecyclerView, como en que posición se esta mostrado nuestra vista, si esa vista esta reciclada o no, si se puede reutilizar para que el RecyclerView pueda disponer de ellas cuando las necesite.

Para ello nos tenemos que crear una nueva clase que extienda de Disolver, se puede crear en cualquier sitio que nos interese, yo normalmente lo suelo hacer adentro de nuestra clase recién definida y ademas la llamo ViewHolder que extiende de RecyclerView.ViewHolder, esto reciben un único argumento, que es la visa que va a almacenar este ViewHolder, así que se la vamos a pasar por el constructor al ViewHolder y el ViewHolder la va a recibir por el constructor de nuestra clase heredada.

Con esto ya nos podemos crear el Adapter, que va a usar como tipo genérico el ViewHolder que acabamos de crear, además de ser una clase y estar extendiendo, tenemos que llamar al constructor padre, este constructor no requiere ningún parámetro.

El Adapter es una clase Abstracta lo que indica que es una clase que no se puede implementar y además esa clase puede tener métodos que las clases que lo extienden necesiten implementar, este es el caso de MoviesAdapter que si nos fijamos nos va a obligar a implementar tres métodos y ahora veremos que significa cada uno de estos métodos

onCreateViewHolder() se va a llamar cuando el RecyclerView requiera de una nueva vista para mostrar algo en pantalla porque no existan vistas que podamos reciclar.

onViewHolder() se va a llamar siempre que necesitemos actualizar los datos de una vista, independiente de si esta se ha creado por primera vez o se esta reciclando, porque ha salido por pantalla y la podemos volver a usar.

getItemCount() que nos indica el numero de elementos que tiene la lista.

Para ver mejor lo dicho anteriormente haremos que nuestra clase reciba un listado de películas.

package com.antonioleiva.androiddesdecero

import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class MoviesAdapter(private val movies: List<Movie>): RecyclerView.Adapter<MoviesAdapter.ViewHolder>() {

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

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

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

    class ViewHolder(view: View): RecyclerView.ViewHolder(view)
}

Y luego lo único que tenemos que hacer en nuestro método getItemCount() es devolver el numero de items de la lista. Añadiendo a esto cuando queremos hacer que una función o en nuestro caso nuestro método haremos que tenga el operador igual

override fun getItemCount(): Int = movies.size

Nos quedaría hasta este punto así:

y ahora vamos a empezar a implementar los dos métodos que nos hacen falta; onCreateViewHolder() donde inflaremos la vista y para eso vamos a utilizar el LayoutInflater para cargar la vista personalizada desde el XML; en el onBindViewHolder() le vamos asignar la película que le corresponda a esa vista.

Aquí inflamos nuestra vista y devolvemos un ViewHolder()

Ahora tenemos que recuperar la película que queremos mostrar en esa posición en el onBindViewHolder() y luego pasársela al ViewHolder para que actualice los datos correspondientes a esa vista.

package com.antonioleiva.androiddesdecero

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.bumptech.glide.Glide

class MovieView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {

    private val cover: ImageView
    private val title: TextView

    init {
        val view = LayoutInflater
            .from(context)
            .inflate(R.layout.view_movie, this, true)

        cover = findViewById(R.id.cover)
        title = findViewById(R.id.title)

        orientation = VERTICAL
    }

    fun setMovie(movie: Movie) {
        title.text = movie.title
        Glide.with(context).load(movie.cover).into(cover)
    }
}

Ahora nos vamos al MainActivity para asignarle un listado de valores.

package com.antonioleiva.androiddesdecero

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.RecyclerView

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

        val recycler = findViewById<RecyclerView>(R.id.recycler)
        recycler.adapter = MoviesAdapter(listOf(
            Movie("Movie 1", "https://loremflickr.com/320/240?lock=1"),
            Movie("Movie 2", "https://loremflickr.com/320/240?lock=2"),
            Movie("Movie 3", "https://loremflickr.com/320/240?lock=3"),
            Movie("Movie 4", "https://loremflickr.com/320/240?lock=4"),
            Movie("Movie 5", "https://loremflickr.com/320/240?lock=5"),
            Movie("Movie 6", "https://loremflickr.com/320/240?lock=6"),
            Movie("Movie 7", "https://loremflickr.com/320/240?lock=7"),
            Movie("Movie 8", "https://loremflickr.com/320/240?lock=8"),
            Movie("Movie 9", "https://loremflickr.com/320/240?lock=9"),


        ))

        // val movie = findViewById<MovieView>(R.id.movie)
        // movie.setMovie(Movie("Batman", "https://loremflickr.com/320/240"))
    }

}

Ya por ultimo solo tenemos que ejecutar nuestra aplicación y nos debería mostrar algo así

Partes de un RecyclerView

Dentro del RecyclerView podemos encontrar al menos 5 componentes:

El propio RecyclerView

Es el componente principal que se encarga de utilizar al resto para renderizar las vistas en pantalla de forma adecuada

Adapter

Provee al RecyclerView de las vistas que necesita pintar para cada uno de los componentes, y le indica también cómo utilizar los datos para actualizar la apariencia de la vista.

ViewHolder

Ayuda al Adapter guardando la vista que se va a mostrar. Es realmente el componente que se recicla, y guarda más información extra, como la posición del RecyclerView en la que se encuentra.

LayoutManager

El LayoutManager: identifica la posición de cada una de las celdas dentro del RecyclerView. Hay varios por defecto, pero técnicamente podríamos crearnos el nuestro propio que cubra nuestras necesidades.

Esto segundo es realmente difícil, pero la buena noticia es que la mayoría de los casos se pueden cubrir con los que hay por defecto.

Sobre el LayoutManager haré un artículo aparte.

ItemAnimator

Este componente ayuda a realizar animaciones cuando se producen cambios en los elementos de la lista. Podemos hacer muchas cosas con las animaciones, pero nuevamente no es algo especialmente sencillo.

La realidad es que usando un componente llamado DiffUtil (del que también publicaré un artículo), normalmente no vamos a necesitar tocar nada aquí, a no ser que queramos animaciones muy personalizadas.

Quizá también te interese…

3 formas de pasar varios Listeners a un RecyclerView

3 formas de pasar varios Listeners a un RecyclerView

Seguro que has visto muchos ejemplos donde un RecyclerView recibe un listener para, por ejemplo realizar, una acción cuando se hace click en el elemento. class MoviesAdapter(private val listener: (Movie) -> Unit) : ListAdapter<Movie, MoviesAdapter.ViewHolder>(...)...

Clases y constructores en Kotlin con Android Studio

Clases y constructores en Kotlin con Android Studio

Veremos un repaso las clases y constructores de Kotlin para solventar esas inquietudes que nos surgen a la hora de seguir este curso, que pueden ser como funciona realmente y porque se presentan las clases de ese modo, como es la interacción y el constructor a la hora...

Temas, colores, tipografías y formas en Jetpack Compose

Temas, colores, tipografías y formas en Jetpack Compose

Si vienes del sistema clásico de vistas, recordarás que toda la definición de temas se hacía de una forma bastante tediosa a través de styles en XML. Si odias tu vida (o estás en una App donde mezclas XMLs y Jetpack Compose), aún puedes seguir usando esos temas...

0 comentarios

Enviar un comentario

Los datos personales que proporciones a través de este formulario quedarán registrados en un fichero de Antonio Leiva Gordillo, con el fin de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento de la parte interesada. Si no se acepta, no podrás comentar en este blog. Los datos que proporciona solo se utilizan para evitar el correo no deseado y no se usarán para nada más. Puede ejercer los derechos de acceso, rectificación, cancelación y oposición en contacto@devexperto.com.

Tu dirección de correo electrónico no será publicada.

Acepto la política de privacidad *