RecyclerView es un componente de Android Jetpack para mostrar eficientemente grandes conjuntos de datos en forma de listas y cuadrículas. A diferencia de ListView, RecyclerView reutiliza los ViewHolders y gestiona el diseño a través de LayoutManager. Según Google (Android Developers, 2026), RecyclerView procesa hasta 60 fotogramas por segundo al desplazar una lista de más de 1000 elementos gracias a la reutilización de ViewHolders y el algoritmo de diferencias DiffUtil. Obtén más información sobre la arquitectura de aplicaciones Android en nuestro artículo sobre ListView.
Puntos clave
RecyclerView es un componente de la biblioteca Android Jetpack, presentado en Google I/O 2014 como parte de Android Support Library v7. Está diseñado para mostrar listas y cuadrículas dinámicas con alto rendimiento. RecyclerView surgió como reemplazo de ListView y resuelve el problema clave de su predecesor: la falta de reutilización obligatoria de ViewHolders.
La arquitectura de RecyclerView se basa en el patrón Model-View-Adapter: los datos se almacenan en un modelo (List, LiveData, Flow), la representación la gestiona el Adapter y la disposición la controla LayoutManager. Cada componente es responsable de su área, lo que hace que el sistema sea flexible y extensible.
Según una encuesta de Android Developers (2025), RecyclerView se utiliza en el 78% de las aplicaciones Android del top 100 de Google Play. El rendimiento de desplazamiento para una lista de más de 1000 elementos se mantiene estable a 60 FPS en dispositivos de gama media gracias a tres mecanismos: la reutilización de ViewHolders, el cálculo de actualizaciones basado en diferencias y la carga asíncrona de datos.
RecyclerView es óptimo para: listas con cantidad desconocida de elementos (feeds, chats, registros), cuadrículas de N columnas (galerías, catálogos), diseños escalonados (tableros Pinterest) y listas con animaciones de agregar/eliminar. Para pantallas simples con 2–5 elementos, LinearLayout o ScrollView son más simples y eficientes — RecyclerView añade sobrecarga.
Para usar RecyclerView, agrega la dependencia a build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. La versión mínima de API es 14 (Android 4.0). A partir de AndroidX RecyclerView 1.3.0, están disponibles los módulos recyclerview-selection para selección de elementos y recyclerview-swipe para gestos de deslizamiento.
RecyclerView se divide en tres capas independientes: el Adapter gestiona los datos y ViewHolders, el LayoutManager maneja el posicionamiento de elementos y el ItemAnimator controla las animaciones. Esta arquitectura permite reemplazar cualquier capa sin modificar las demás — por ejemplo, cambiar de LinearLayoutManager a GridLayoutManager sin cambiar el código del Adapter.
| Componente | Responsabilidades | Implementación |
|---|---|---|
| Adapter | Crear ViewHolders, vincular datos a View, notificar cambios | RecyclerView.Adapter<VH> |
| ViewHolder | Almacenar en caché referencias a View, evitar findViewById() | RecyclerView.ViewHolder |
| LayoutManager | Posicionar elementos, determinar dirección de desplazamiento | LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager |
| ItemAnimator | Animación al agregar, eliminar, mover elementos | DefaultItemAnimator |
| ItemDecoration | Dibujar separadores, espaciados, fondos entre elementos | RecyclerView.ItemDecoration |
Ciclo de trabajo: (1) Adapter crea un ViewHolder mediante onCreateViewHolder(), (2) LayoutManager determina la posición del nuevo View en pantalla, (3) Adapter vincula datos mediante onBindViewHolder(), (4) durante el desplazamiento, los ViewHolders que salen de la pantalla entran al pool (RecycledViewPool) para reutilización. En lugar de crear nuevos Views, se reutilizan los existentes — este es el mecanismo clave de rendimiento.
ViewHolder es una clase que almacena referencias a Views (TextView, ImageView, etc.) dentro de un elemento de lista. Sin ViewHolder, cada desplazamiento activaría findViewById() — una de las operaciones más costosas de la UI de Android. ViewHolder elimina esta llamada almacenando en caché las referencias después de la vinculación inicial.
// Modelo de datos
data class Article(
title: String,
summary: String,
author: String,
imageUrl: String?
)
// ViewHolder — almacena referencias a View
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)
fun bind(article: Article) {
titleText.text = article.title
summaryText.text = article.summary
authorText.text = article.author
}
}
Regla práctica: cada View en el diseño del elemento debe declararse como propiedad del ViewHolder con findViewById() exactamente una vez — en el constructor. El método bind() no llama a findViewById(), solo establece texto, colores y listeners. Este enfoque acelera el desplazamiento de la lista en un 30–50% según Google (Android Performance Patterns, 2025).
LayoutManager define cómo se posicionan los elementos dentro de RecyclerView. Android proporciona tres implementaciones predeterminadas: LinearLayoutManager, GridLayoutManager y StaggeredGridLayoutManager. La elección de LayoutManager afecta la percepción de los datos y el rendimiento del desplazamiento.
| LayoutManager | Disposición | Cuándo usarlo | Método de configuración |
|---|---|---|---|
| LinearLayoutManager | Lista (vertical u horizontal) | Feeds, chats, notificaciones, listas de productos | LinearLayoutManager(context) |
| GridLayoutManager | Cuadrícula de N columnas | Galerías, catálogos, iconos, cuadrículas de imágenes | GridLayoutManager(context, spanCount) |
| StaggeredGridLayoutManager | Cuadrícula escalonada (diferentes alturas de elementos) | Tableros Pinterest, notas, galerías masonry | StaggeredGridLayoutManager(spanCount, orientation) |
// Configurar LinearLayoutManager en Fragment
class ArticleListFragment : Fragment() {
private lateinit var binding: FragmentArticleListBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = ArticleAdapter(articlesList)
// Optimización: tamaño fijo + pool
recyclerView.setHasFixedSize(true)
recyclerView.setItemViewCacheSize(20)
// Separador entre elementos
val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(divider)
}
}
Rendimiento: LinearLayoutManager con desplazamiento vertical es la opción más rápida porque los elementos se colocan secuencialmente y LayoutManager calcula posiciones en O(1). GridLayoutManager requiere cálculos adicionales para dividir en columnas pero sigue siendo eficiente con un spanCount fijo. StaggeredGridLayoutManager es el más lento de los tres porque necesita calcular la posición de los elementos en cascada considerando las alturas de los elementos vecinos. Úsalo solo cuando se requieran alturas de celda visualmente diferentes.
Adapter en RecyclerView implementa el patrón Adapter de GoF: transforma datos (List<T>) en ViewHolders mostrados en pantalla. Cuando los datos cambian, el Adapter puede notificar a RecyclerView mediante notifyDataSetChanged(), notifyItemInserted() y métodos similares. El problema es que notifyDataSetChanged() redibuja todos los elementos visibles, causando tirones (jank).
// Adapter con DiffUtil para actualizaciones eficientes
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {
private var articles: List<Article> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_article, parent, false)
return ArticleViewHolder(view)
}
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
holder.bind(articles[position])
}
override fun getItemCount(): Int = articles.size
// Actualizar lista mediante DiffUtil
fun updateArticles(newArticles: List<Article>) {
val diffCallback = ArticleDiffCallback(articles, newArticles)
val diffResult = DiffUtil.calculateDiff(diffCallback)
articles = newArticles
diffResult.dispatchUpdatesTo(this)
}
}
// Callback para DiffUtil
class ArticleDiffCallback(
private val oldList: List<Article>,
private val newList: List<Article>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos].title == newList[newPos].title
}
override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos] == newList[newPos]
}
}
DiffUtil utiliza el algoritmo de diferencias de Eugene W. Myers, que calcula el número mínimo de operaciones (insertar, eliminar, mover, cambiar) para transformar la lista antigua en la nueva. Para una lista de 100 elementos, DiffUtil funciona en 1–5 ms (Google, 2025). Para listas grandes (10000+), usa AsyncListDiffer o PagingDataAdapter de la biblioteca Paging — realizan cálculos en un hilo en segundo plano.
ListView es el predecesor de RecyclerView, introducido en Android 1.0 (API 1). También muestra listas pero no obliga al uso de ViewHolder, no soporta LayoutManager y no puede animar cambios. RecyclerView fue diseñado como reemplazo de ListView con una arquitectura más estricta y mejor rendimiento.
| Criterio | RecyclerView | ListView |
|---|---|---|
| ViewHolder | Obligatorio (arquitectura) | Recomendado pero no requerido |
| LayoutManager | Separación: lista, cuadrícula, escalonada | Solo lista vertical |
| Animación de cambios | DefaultItemAnimator integrado | Sin soporte incorporado |
| Notificación de cambios | Granular: notifyItemInserted/Removed | Solo notifyDataSetChanged() para toda la lista |
| DiffUtil | Soporte mediante DiffUtil.Callback | Sin equivalente |
| Separadores | ItemDecoration (personalizado) | android:divider (integrado) |
| Versión mínima | API 14 (Android 4.0) mediante AndroidX | API 1 (todas las versiones) |
Recomendación de Google (Android Developers, 2026): usa RecyclerView para todos los proyectos nuevos. ListView puede permanecer en bases de código heredadas donde la migración no justifique el costo. RecyclerView ofrece mejor rendimiento con más de 100 elementos, soporta desplazamiento horizontal y cuadrículas sin soluciones adicionales. En Android 15, ListView está oficialmente marcado como parcialmente obsoleto.
Preguntas frecuentes
Verifica tres condiciones: (1) Adapter está configurado mediante recyclerView.adapter = adapter, (2) LayoutManager está configurado mediante recyclerView.layoutManager = LinearLayoutManager(context), (3) la lista de datos no está vacía y el adapter ha sido notificado (notifyDataSetChanged()). Un error típico es establecer los datos después de llamar a notifyDataSetChanged(). Los datos deben asignarse antes de la notificación. Comprueba el tamaño de la lista: adapter.itemCount debe ser mayor que 0.
La lentitud durante el desplazamiento (jank) ocurre cuando onBindViewHolder() realiza operaciones pesadas: carga de imágenes sin caché, findViewById() dentro de bind, cálculos complejos. Soluciones: (1) usa Glide o Coil para carga asíncrona de imágenes, (2) mueve todas las llamadas findViewById() al constructor de ViewHolder, (3) para cálculos de diferencias, usa AsyncListDiffer. Activa la creación de perfiles de GPU en Android Studio (Profile GPU Rendering) para encontrar fotogramas lentos.
Pasa la orientación a LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). El segundo parámetro es reverseLayout (false = izquierda a derecha). Para una cuadrícula horizontal, usa GridLayoutManager con orientación horizontal o un LinearSnapHelper personalizado para desplazamiento por páginas. Las listas horizontales funcionan bien para carruseles de productos, imágenes, categorías.
RecyclerView no tiene un onClickListener incorporado (a diferencia de ListView con onItemClickListener). Implméntalo en ViewHolder: pasa una lambda al constructor y establece itemView.setOnClickListener. Ejemplo: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Llama a onItemClick(article) dentro de bind(). Una alternativa es usar RecyclerView.addOnItemTouchListener() para manejo de gestos.
RecycledViewPool es un pool interno de ViewHolders que se han vuelto invisibles durante el desplazamiento. En lugar de ser destruidos, se mantienen en el pool y se reutilizan. Por defecto, el pool almacena 5 ViewHolders de cada tipo. Aumenta el tamaño para diseños complejos: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Si un RecyclerView tiene múltiples tipos de View (viewType), el pool mantiene pilas separadas para cada uno. Compartir un RecycledViewPool entre diferentes RecyclerViews ahorra memoria en pantallas con listas anidadas.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también