RecyclerView é um componente do Android Jetpack para exibir eficientemente grandes conjuntos de dados na forma de listas e grades. Ao contrário do ListView, o RecyclerView reutiliza ViewHolders e gerencia o layout através do LayoutManager. De acordo com o Google (Android Developers, 2026), o RecyclerView processa até 60 quadros por segundo ao rolar uma lista de mais de 1000 itens graças ao pool de ViewHolders e ao algoritmo de diferenças DiffUtil. Saiba mais sobre arquitetura de aplicativos Android em nosso artigo sobre ListView.
Pontos principais
RecyclerView é um componente da biblioteca Android Jetpack, apresentado no Google I/O 2014 como parte do Android Support Library v7. Ele é projetado para exibir listas e grades dinâmicas com alto desempenho. O RecyclerView surgiu como substituto do ListView e resolve o problema principal do seu predecessor — a falta de reutilização forçada de ViewHolders.
A arquitetura do RecyclerView é baseada no padrão Model-View-Adapter: os dados são armazenados em um modelo (List, LiveData, Flow), a renderização é gerenciada pelo Adapter e o layout é controlado pelo LayoutManager. Cada componente é responsável por sua área, tornando o sistema flexível e extensível.
De acordo com uma pesquisa do Android Developers (2025), o RecyclerView é usado em 78% dos aplicativos Android no top 100 do Google Play. O desempenho de rolagem para uma lista de mais de 1000 itens permanece estável a 60 FPS em dispositivos de médio porte graças a três mecanismos: pool de ViewHolders, cálculo de atualização baseado em diferenças e carregamento assíncrono de dados.
RecyclerView é ideal para: listas com número desconhecido de itens (feeds, chats, logs), grades de N colunas (galerias, catálogos), layouts escalonados (quadros Pinterest) e listas com animações de adicionar/remover. Para telas simples com 2–5 itens, LinearLayout ou ScrollView são mais simples e eficientes — RecyclerView adiciona sobrecarga.
Para usar RecyclerView, adicione a dependência ao build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. A versão mínima da API é 14 (Android 4.0). A partir do AndroidX RecyclerView 1.3.0, os módulos recyclerview-selection para seleção de itens e recyclerview-swipe para gestos de deslize estão disponíveis.
RecyclerView é dividido em três camadas independentes: o Adapter gerencia dados e ViewHolders, o LayoutManager lida com o posicionamento dos itens e o ItemAnimator controla as animações. Essa arquitetura permite substituir qualquer camada sem modificar as outras — por exemplo, mudar de LinearLayoutManager para GridLayoutManager sem alterar o código do Adapter.
| Componente | Responsabilidades | Implementação |
|---|---|---|
| Adapter | Criar ViewHolders, vincular dados à View, notificar sobre mudanças | RecyclerView.Adapter<VH> |
| ViewHolder | Armazenar em cache referências de View, evitar findViewById() | RecyclerView.ViewHolder |
| LayoutManager | Posicionar itens, determinar direção de rolagem | LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager |
| ItemAnimator | Animação para adicionar, remover, mover itens | DefaultItemAnimator |
| ItemDecoration | Desenhar divisores, espaçamentos, fundos entre itens | RecyclerView.ItemDecoration |
Ciclo de trabalho: (1) Adapter cria um ViewHolder via onCreateViewHolder(), (2) LayoutManager determina a posição do novo View na tela, (3) Adapter vincula dados via onBindViewHolder(), (4) durante a rolagem, ViewHolders que saem da tela entram no pool (RecycledViewPool) para reutilização. Em vez de criar novos Views, os existentes são reutilizados — este é o principal mecanismo de desempenho.
ViewHolder é uma classe que armazena referências a Views (TextView, ImageView, etc.) dentro de um item de lista. Sem ViewHolder, cada rolagem ativaria findViewById() — uma das operações mais caras da UI Android. O ViewHolder elimina essa chamada armazenando em cache as referências após a vinculação inicial.
// Modelo de dados
data class Article(
title: String,
summary: String,
author: String,
imageUrl: String?
)
// ViewHolder — armazena referências 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
}
}
Regra prática: cada View no layout do item deve ser declarada como propriedade do ViewHolder com findViewById() exatamente uma vez — no construtor. O método bind() não chama findViewById(), apenas define texto, cores e listeners. Essa abordagem acelera a rolagem da lista em 30–50% de acordo com o Google (Android Performance Patterns, 2025).
LayoutManager define como os itens são posicionados dentro do RecyclerView. O Android fornece três implementações padrão: LinearLayoutManager, GridLayoutManager e StaggeredGridLayoutManager. A escolha do LayoutManager afeta a percepção dos dados e o desempenho da rolagem.
| LayoutManager | Disposição | Quando usar | Método de configuração |
|---|---|---|---|
| LinearLayoutManager | Lista (vertical ou horizontal) | Feeds, chats, notificações, listas de produtos | LinearLayoutManager(context) |
| GridLayoutManager | Grade de N colunas | Galeria, catálogos, ícones, grades de imagens | GridLayoutManager(context, spanCount) |
| StaggeredGridLayoutManager | Grade escalonada (alturas diferentes de itens) | Quadros Pinterest, notas, galerias masonry | StaggeredGridLayoutManager(spanCount, orientation) |
// Configurar LinearLayoutManager em 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)
// Otimização: tamanho fixo + pool
recyclerView.setHasFixedSize(true)
recyclerView.setItemViewCacheSize(20)
// Divisor entre itens
val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(divider)
}
}
Desempenho: LinearLayoutManager com rolagem vertical é a opção mais rápida porque os itens são dispostos sequencialmente e o LayoutManager calcula posições em O(1). GridLayoutManager requer cálculos adicionais para divisão em colunas, mas permanece eficiente com um spanCount fixo. StaggeredGridLayoutManager é o mais lento dos três, pois precisa calcular a posição dos itens em cascata considerando as alturas dos itens vizinhos. Use-o apenas quando alturas de célula visualmente diferentes forem necessárias.
Adapter no RecyclerView implementa o padrão Adapter do GoF: transforma dados (List<T>) em ViewHolders exibidos na tela. Quando os dados mudam, o Adapter pode notificar o RecyclerView via notifyDataSetChanged(), notifyItemInserted() e métodos similares. O problema é que notifyDataSetChanged() redesenha todos os itens visíveis, causando travamentos (jank).
// Adapter com DiffUtil para atualizações 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
// Atualizar lista via 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 usa o algoritmo de diferenças de Eugene W. Myers, que calcula o número mínimo de operações (inserir, remover, mover, alterar) para transformar a lista antiga na nova. Para uma lista de 100 itens, o DiffUtil funciona em 1–5 ms (Google, 2025). Para listas grandes (10000+), use AsyncListDiffer ou PagingDataAdapter da biblioteca Paging — eles realizam cálculos em uma thread em segundo plano.
ListView é o predecessor do RecyclerView, introduzido no Android 1.0 (API 1). Ele também exibe listas, mas não força o uso de ViewHolder, não suporta LayoutManager e não pode animar mudanças. O RecyclerView foi projetado como substituto do ListView com uma arquitetura mais rigorosa e melhor desempenho.
| Critério | RecyclerView | ListView |
|---|---|---|
| ViewHolder | Obrigatório (arquitetura) | Recomendado, mas não obrigatório |
| LayoutManager | Separação: lista, grade, escalonada | Apenas lista vertical |
| Animação de mudanças | DefaultItemAnimator integrado | Sem suporte nativo |
| Notificação de mudanças | Granular: notifyItemInserted/Removed | Apenas notifyDataSetChanged() para toda a lista |
| DiffUtil | Suporte via DiffUtil.Callback | Sem equivalente |
| Divisores | ItemDecoration (personalizado) | android:divider (integrado) |
| Versão mínima | API 14 (Android 4.0) via AndroidX | API 1 (todas as versões) |
Recomendação do Google (Android Developers, 2026): use RecyclerView para todos os novos projetos. ListView pode permanecer em bases de código legadas onde a migração não justifica o custo. RecyclerView oferece melhor desempenho com 100+ itens, suporta rolagem horizontal e grades sem soluções alternativas. No Android 15, o ListView está oficialmente marcado como parcialmente obsoleto.
Perguntas frequentes
Verifique três condições: (1) Adapter está definido via recyclerView.adapter = adapter, (2) LayoutManager está definido via recyclerView.layoutManager = LinearLayoutManager(context), (3) a lista de dados não está vazia e o adapter foi notificado (notifyDataSetChanged()). Um erro típico é definir os dados após chamar notifyDataSetChanged(). Os dados devem ser atribuídos antes da notificação. Verifique o tamanho da lista: adapter.itemCount deve ser maior que 0.
A lentidão durante a rolagem (jank) ocorre quando onBindViewHolder() executa operações pesadas: carregamento de imagens sem cache, findViewById() dentro de bind, cálculos complexos. Soluções: (1) use Glide ou Coil para carregamento assíncrono de imagens, (2) mova todas as chamadas findViewById() para o construtor do ViewHolder, (3) para cálculos de diferenças, use AsyncListDiffer. Ative a criação de perfil de GPU no Android Studio (Profile GPU Rendering) para encontrar quadros lentos.
Passe a orientação para o LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). O segundo parâmetro é reverseLayout (false = esquerda para direita). Para uma grade horizontal, use GridLayoutManager com orientação horizontal ou um LinearSnapHelper personalizado para rolagem paginada. Listas horizontais funcionam bem para carrosséis de produtos, imagens, categorias.
RecyclerView não tem um onClickListener integrado (ao contrário do ListView com onItemClickListener). Implemente-o no ViewHolder: passe uma lambda para o construtor e defina itemView.setOnClickListener. Exemplo: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Chame onItemClick(article) dentro de bind(). Uma alternativa é usar RecyclerView.addOnItemTouchListener() para manipulação de gestos.
RecycledViewPool é um pool interno de ViewHolders que se tornaram invisíveis durante a rolagem. Em vez de serem destruídos, eles são mantidos no pool e reutilizados. Por padrão, o pool armazena 5 ViewHolders de cada tipo. Aumente o tamanho para layouts complexos: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Se um RecyclerView tiver vários tipos de View (viewType), o pool mantém pilhas separadas para cada um. Compartilhar um RecycledViewPool entre diferentes RecyclerViews economiza memória em telas com listas aninhadas.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também