RecyclerView è un componente Android Jetpack per visualizzare efficientemente grandi insiemi di dati sotto forma di elenchi e griglie. A differenza di ListView, RecyclerView riutilizza i ViewHolder e gestisce il layout tramite LayoutManager. Secondo Google (Android Developers, 2026), RecyclerView elabora fino a 60 fotogrammi al secondo durante lo scorrimento di un elenco di oltre 1000 elementi grazie al pooling dei ViewHolder e all'algoritmo di differenze DiffUtil. Ulteriori informazioni sull'architettura delle app Android sono disponibili in il nostro articolo su ListView.
Punti chiave
RecyclerView è un componente della libreria Android Jetpack, presentato al Google I/O 2014 come parte di Android Support Library v7. È progettato per visualizzare elenchi e griglie dinamiche con alte prestazioni. RecyclerView è nato come sostituto di ListView e risolve il problema principale del suo predecessore — la mancanza di riutilizzo forzato dei ViewHolder.
L'architettura di RecyclerView si basa sul pattern Model-View-Adapter: i dati sono memorizzati in un modello (List, LiveData, Flow), il rendering è gestito dall'Adapter e il layout è controllato da LayoutManager. Ogni componente è responsabile della propria area, rendendo il sistema flessibile ed estensibile.
Secondo un sondaggio di Android Developers (2025), RecyclerView è utilizzato nel 78% delle app Android nella top 100 di Google Play. Le prestazioni di scorrimento per un elenco di oltre 1000 elementi rimangono stabili a 60 FPS sui dispositivi di fascia media grazie a tre meccanismi: pooling dei ViewHolder, calcolo degli aggiornamenti basato sulle differenze e caricamento asincrono dei dati.
RecyclerView è ottimale per: elenchi con numero sconosciuto di elementi (feed, chat, log), griglie a N colonne (gallerie, cataloghi), layout a cascata (bacheche Pinterest) ed elenchi con animazioni di aggiunta/rimozione. Per schermate semplici con 2–5 elementi, LinearLayout o ScrollView sono più semplici ed efficienti — RecyclerView aggiunge overhead.
Per utilizzare RecyclerView, aggiungi la dipendenza a build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. La versione API minima è 14 (Android 4.0). A partire da AndroidX RecyclerView 1.3.0, sono disponibili i moduli recyclerview-selection per la selezione degli elementi e recyclerview-swipe per i gesti di scorrimento.
RecyclerView è diviso in tre livelli indipendenti: l'Adapter gestisce i dati e i ViewHolder, il LayoutManager si occupa del posizionamento degli elementi e l'ItemAnimator controlla le animazioni. Questa architettura consente di sostituire qualsiasi livello senza modificare gli altri — ad esempio, passare da LinearLayoutManager a GridLayoutManager senza cambiare il codice dell'Adapter.
| Componente | Responsabilità | Implementazione |
|---|---|---|
| Adapter | Creare ViewHolder, collegare i dati alla View, notificare le modifiche | RecyclerView.Adapter<VH> |
| ViewHolder | Memorizzare nella cache i riferimenti alle View, evitare findViewById() | RecyclerView.ViewHolder |
| LayoutManager | Posizionare gli elementi, determinare la direzione di scorrimento | LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager |
| ItemAnimator | Animazione per aggiunta, rimozione, spostamento di elementi | DefaultItemAnimator |
| ItemDecoration | Disegnare divisori, spaziature, sfondi tra gli elementi | RecyclerView.ItemDecoration |
Ciclo di lavoro: (1) L'Adapter crea un ViewHolder tramite onCreateViewHolder(), (2) LayoutManager determina la posizione del nuovo View sullo schermo, (3) L'Adapter collega i dati tramite onBindViewHolder(), (4) durante lo scorrimento, i ViewHolder che escono dallo schermo entrano nel pool (RecycledViewPool) per il riutilizzo. Invece di creare nuovi View, quelli esistenti vengono riutilizzati — questo è il meccanismo chiave delle prestazioni.
ViewHolder è una classe che memorizza i riferimenti alle View (TextView, ImageView, ecc.) all'interno di un elemento dell'elenco. Senza ViewHolder, ogni scorrimento attiverebbe findViewById() — una delle operazioni più costose dell'interfaccia Android. ViewHolder elimina questa chiamata memorizzando nella cache i riferimenti dopo il binding iniziale.
// Modello dati
data class Article(
title: String,
summary: String,
author: String,
imageUrl: String?
)
// ViewHolder — memorizza riferimenti 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
}
}
Regola pratica: ogni View nel layout dell'elemento deve essere dichiarata come proprietà ViewHolder con findViewById() esattamente una volta — nel costruttore. Il metodo bind() non chiama findViewById(), imposta solo testo, colori e listener. Questo approccio accelera lo scorrimento dell'elenco del 30–50% secondo Google (Android Performance Patterns, 2025).
LayoutManager definisce come gli elementi sono posizionati all'interno di RecyclerView. Android fornisce tre implementazioni predefinite: LinearLayoutManager, GridLayoutManager e StaggeredGridLayoutManager. La scelta del LayoutManager influisce sulla percezione dei dati e sulle prestazioni di scorrimento.
| LayoutManager | Disposizione | Quando usarlo | Metodo di configurazione |
|---|---|---|---|
| LinearLayoutManager | Elenco (verticale o orizzontale) | Feed, chat, notifiche, elenchi di prodotti | LinearLayoutManager(context) |
| GridLayoutManager | Griglia a N colonne | Gallerie, cataloghi, icone, griglie di immagini | GridLayoutManager(context, spanCount) |
| StaggeredGridLayoutManager | Griglia a cascata (altezze diverse degli elementi) | Bacheche Pinterest, note, gallerie masonry | StaggeredGridLayoutManager(spanCount, orientation) |
// Configurare LinearLayoutManager in 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)
// Ottimizzazione: dimensione fissa + pool
recyclerView.setHasFixedSize(true)
recyclerView.setItemViewCacheSize(20)
// Divisore tra elementi
val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(divider)
}
}
Prestazioni: LinearLayoutManager con scorrimento verticale è l'opzione più veloce perché gli elementi sono disposti sequenzialmente e LayoutManager calcola le posizioni in O(1). GridLayoutManager richiede calcoli aggiuntivi per la divisione in colonne ma rimane efficiente con un spanCount fisso. StaggeredGridLayoutManager è il più lento dei tre poiché deve calcolare la posizione degli elementi a cascata considerando le altezze degli elementi vicini. Usalo solo quando sono visivamente richieste altezze di cella diverse.
Adapter in RecyclerView implementa il pattern Adapter del GoF: trasforma i dati (List<T>) in ViewHolder visualizzati sullo schermo. Quando i dati cambiano, l'Adapter può notificare RecyclerView tramite notifyDataSetChanged(), notifyItemInserted() e metodi simili. Il problema è che notifyDataSetChanged() ridisegna tutti gli elementi visibili, causando scatti (jank).
// Adapter con DiffUtil per aggiornamenti efficienti
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
// Aggiornare elenco tramite DiffUtil
fun updateArticles(newArticles: List<Article>) {
val diffCallback = ArticleDiffCallback(articles, newArticles)
val diffResult = DiffUtil.calculateDiff(diffCallback)
articles = newArticles
diffResult.dispatchUpdatesTo(this)
}
}
// Callback per 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 utilizza l'algoritmo di differenze di Eugene W. Myers, che calcola il numero minimo di operazioni (inserisci, rimuovi, sposta, modifica) per trasformare l'elenco vecchio in quello nuovo. Per un elenco di 100 elementi, DiffUtil funziona in 1–5 ms (Google, 2025). Per elenchi di grandi dimensioni (10000+), utilizza AsyncListDiffer o PagingDataAdapter della libreria Paging — eseguono i calcoli in un thread in background.
ListView è il predecessore di RecyclerView, introdotto in Android 1.0 (API 1). Visualizza anche elenchi ma non impone ViewHolder, non supporta LayoutManager e non può animare le modifiche. RecyclerView è stato progettato come sostituto di ListView con un'architettura più rigorosa e prestazioni migliori.
| Criterio | RecyclerView | ListView |
|---|---|---|
| ViewHolder | Obbligatorio (architettura) | Raccomandato ma non obbligatorio |
| LayoutManager | Separazione: elenco, griglia, cascata | Solo elenco verticale |
| Animazione modifiche | DefaultItemAnimator integrato | Nessun supporto integrato |
| Notifica modifiche | Granulare: notifyItemInserted/Removed | Solo notifyDataSetChanged() per l'intero elenco |
| DiffUtil | Supporto tramite DiffUtil.Callback | Nessun equivalente |
| Divisori | ItemDecoration (personalizzato) | android:divider (integrato) |
| Versione minima | API 14 (Android 4.0) tramite AndroidX | API 1 (tutte le versioni) |
Raccomandazione Google (Android Developers, 2026): utilizza RecyclerView per tutti i nuovi progetti. ListView può rimanere in basi di codice legacy dove la migrazione non giustifica il costo. RecyclerView offre prestazioni migliori con 100+ elementi, supporta lo scorrimento orizzontale e le griglie senza soluzioni alternative aggiuntive. In Android 15, ListView è ufficialmente contrassegnato come parzialmente deprecato.
Domande frequenti
Verifica tre condizioni: (1) l'Adapter è impostato tramite recyclerView.adapter = adapter, (2) il LayoutManager è impostato tramite recyclerView.layoutManager = LinearLayoutManager(context), (3) l'elenco dati non è vuoto e l'adapter è stato notificato (notifyDataSetChanged()). Un errore tipico è impostare i dati dopo aver chiamato notifyDataSetChanged(). I dati devono essere assegnati prima della notifica. Controlla la dimensione dell'elenco: adapter.itemCount deve essere maggiore di 0.
La lentezza durante lo scorrimento (jank) si verifica quando onBindViewHolder() esegue operazioni pesanti: caricamento di immagini senza cache, findViewById() dentro bind, calcoli complessi. Soluzioni: (1) utilizza Glide o Coil per il caricamento asincrono delle immagini, (2) sposta tutte le chiamate findViewById() nel costruttore di ViewHolder, (3) per i calcoli delle differenze, utilizza AsyncListDiffer. Attiva la profilazione GPU in Android Studio (Profile GPU Rendering) per trovare i fotogrammi lenti.
Passa l'orientamento a LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). Il secondo parametro è reverseLayout (false = da sinistra a destra). Per una griglia orizzontale, utilizza GridLayoutManager con orientamento orizzontale o un LinearSnapHelper personalizzato per lo scorrimento a pagine. Gli elenchi orizzontali sono adatti per caroselli di prodotti, immagini, categorie.
RecyclerView non ha un onClickListener integrato (a differenza di ListView con onItemClickListener). Implementalo in ViewHolder: passa un lambda al costruttore e imposta itemView.setOnClickListener. Esempio: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Chiama onItemClick(article) dentro bind(). Un'alternativa è utilizzare RecyclerView.addOnItemTouchListener() per la gestione dei gesti.
RecycledViewPool è un pool interno di ViewHolder che sono diventati invisibili durante lo scorrimento. Invece di essere distrutti, vengono conservati nel pool e riutilizzati. Per impostazione predefinita, il pool memorizza 5 ViewHolder per ogni tipo. Aumenta la dimensione per layout complessi: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Se RecyclerView ha più tipi di View (viewType), il pool mantiene pile separate per ciascuno. Condividere un RecycledViewPool tra diversi RecyclerView consente di risparmiare memoria su schermate con elenchi annidati.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche