RecyclerView — komponenta Android Jetpack pro efektivní zobrazení velkých datových sad ve formě seznamů a mřížek. Na rozdíl od ListView RecyclerView znovu používá ViewHoldery a spravuje rozvržení prostřednictvím LayoutManageru. Podle Google (Android Developers, 2026) RecyclerView zpracovává až 60 snímků za sekundu při posouvání seznamu s 1000+ prvky díky poolu ViewHolderů a diferenčnímu algoritmu DiffUtil. Více o architektuře Android aplikací si přečtěte v našem článku o ListView.
Hlavní
RecyclerView — komponenta knihovny Android Jetpack, představená na Google I/O 2014 jako součást Android Support Library v7. Je určen pro zobrazování dynamických seznamů a mřížek s vysokým výkonem. RecyclerView se objevil jako náhrada za ListView a řeší klíčový problém předchůdce — chybějící vynucené znovupoužití ViewHolderů.
Architektura RecyclerView je postavena na vzoru Model-View-Adapter: data jsou uložena v modelu (List, LiveData, Flow), zobrazení spravuje Adapter a rozvržení řídí LayoutManager. Každá komponenta je zodpovědná za svou oblast, což činí systém flexibilním a rozšiřitelným.
Podle průzkumu Android Developers (2025) je RecyclerView používán v 78% Android aplikací z top-100 Google Play. Výkon posouvání seznamu s 1000+ prvky zůstává stabilní na 60 FPS na zařízeních střední třídy díky třem mechanismům: pool ViewHolderů, diferenční výpočet aktualizací a asynchronní načítání dat.
RecyclerView je optimální pro: seznamy s neznámým počtem prvků (feed, chaty, logy), mřížky s N sloupci (galerie, katalogy), kaskádová rozvržení (Pinterest nástěnky), seznamy s animací přidávání/odebírání prvků. Pro jednoduché obrazovky s 2–5 prvky jsou LinearLayout nebo ScrollView jednodušší a efektivnější — RecyclerView přidává režii.
Pro použití RecyclerView přidejte závislost do build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. Minimální verze API — 14 (Android 4.0). Od AndroidX RecyclerView 1.3.0 jsou k dispozici moduly recyclerview-selection pro výběr prvků a recyclerview-swipe pro přejíždění.
RecyclerView je rozdělen do tří nezávislých vrstev: Adapter spravuje data a ViewHoldery, LayoutManager zodpovídá za pozicování prvků, ItemAnimator řídí animace. Tato architektura umožňuje nahradit libovolnou vrstvu bez změny ostatních — například přejít z LinearLayoutManager na GridLayoutManager bez změny kódu Adapteru.
| Komponenta | Odpovědnosti | Implementace |
|---|---|---|
| Adapter | Vytváření ViewHolderů, vázání dat k View, oznamování změn | RecyclerView.Adapter<VH> |
| ViewHolder | Ukládání referencí na View do mezipaměti, vyhýbání se findViewById() | RecyclerView.ViewHolder |
| LayoutManager | Rozmístění prvků, určení směru posouvání | LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager |
| ItemAnimator | Animace přidávání, odebírání, přesouvání prvků | DefaultItemAnimator |
| ItemDecoration | Kreslení oddělovačů, mezer, pozadí mezi prvky | RecyclerView.ItemDecoration |
Pracovní cyklus: (1) Adapter vytvoří ViewHolder pomocí onCreateViewHolder(), (2) LayoutManager určí pozici nového View na obrazovce, (3) Adapter naváže data pomocí onBindViewHolder(), (4) při posouvání se ViewHoldery, které opustily obrazovku, dostanou do poolu (RecycledViewPool) pro opětovné použití. Místo vytváření nových View se znovu používají stávající — to je klíčový výkonnostní mechanismus.
ViewHolder — třída, která ukládá reference na View (TextView, ImageView atd.) uvnitř prvku seznamu. Bez ViewHolder by systém při každém posouvání volal findViewById() — jednu z nejdražších operací Android UI. ViewHolder toto volání eliminuje tím, že si po prvním navázání zapamatuje reference.
// Datový model
data class Article(
title: String,
summary: String,
author: String,
imageUrl: String?
)
// ViewHolder — ukládá reference na 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
}
}
Praktické pravidlo: každý View v rozvržení položky by měl být deklarován jako vlastnost ViewHolder s findViewById() přesně jednou — v konstruktoru. Metoda bind() nevolá findViewById(), pouze nastavuje text, barvy a posluchače. Tento přístup urychluje posouvání seznamu o 30–50% podle Google (Android Performance Patterns, 2025).
LayoutManager určuje, jak jsou prvky rozmístěny uvnitř RecyclerView. Ve výchozím nastavení Android poskytuje tři implementace: LinearLayoutManager, GridLayoutManager a StaggeredGridLayoutManager. Volba LayoutManager ovlivňuje vnímání dat uživatelem a výkon posouvání.
| LayoutManager | Rozmístění | Kdy použít | Metoda nastavení |
|---|---|---|---|
| LinearLayoutManager | Seznam (vertikální nebo horizontální) | Feedy, chaty, oznámení, seznamy produktů | LinearLayoutManager(context) |
| GridLayoutManager | Mřížka s N sloupci | Galerie, katalogy, ikony, mřížka obrázků | GridLayoutManager(context, spanCount) |
| StaggeredGridLayoutManager | Kaskádová mřížka (různá výška prvků) | Pinterest nástěnky, poznámky, masonry galerie | StaggeredGridLayoutManager(spanCount, orientation) |
// Nastavení LinearLayoutManager ve Fragmentu
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)
// Optimalizace: pevná velikost + pool
recyclerView.setHasFixedSize(true)
recyclerView.setItemViewCacheSize(20)
// Oddělovač mezi prvky
val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(divider)
}
}
Výkon: LinearLayoutManager s vertikálním posouváním je nejrychlejší možnost, protože prvky jsou rozmístěny sekvenčně a LayoutManager počítá pozice v O(1). GridLayoutManager vyžaduje dodatečné výpočty pro rozdělení do sloupců, ale zůstává efektivní s pevným spanCount. StaggeredGridLayoutManager je nejpomalejší ze tří, protože musí vypočítat pozici prvku v kaskádě s ohledem na výšku sousedních prvků. Používejte jej jen tehdy, když je vizuálně vyžadována různá výška buněk.
Adapter v RecyclerView implementuje vzor Adapter z GoF: převádí data (List<T>) na ViewHoldery zobrazené na obrazovce. Při změně dat může Adapter upozornit RecyclerView prostřednictvím metod notifyDataSetChanged(), notifyItemInserted() a podobných. Problém je, že notifyDataSetChanged() překresluje všechny viditelné prvky, což vede k trhání (jank).
// Adapter s DiffUtil pro efektivní aktualizaci
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
// Aktualizace seznamu přes DiffUtil
fun updateArticles(newArticles: List<Article>) {
val diffCallback = ArticleDiffCallback(articles, newArticles)
val diffResult = DiffUtil.calculateDiff(diffCallback)
articles = newArticles
diffResult.dispatchUpdatesTo(this)
}
}
// Callback pro 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 používá algoritmus Eugena W. Myerse, který vypočítá minimální počet operací (insert, remove, move, change) pro transformaci starého seznamu na nový. Pro seznam 100 prvků pracuje DiffUtil za 1–5 ms (Google, 2025). Pro velké seznamy (10000+) použijte AsyncListDiffer nebo PagingDataAdapter z knihovny Paging — provádějí výpočty na pozadí.
ListView — předchůdce RecyclerView, objevil se v Android 1.0 (API 1). Také zobrazuje seznamy, ale nevynucuje ViewHolder, nepodporuje LayoutManager a neumí animovat změny. RecyclerView byl navržen jako náhrada za ListView s přísnější architekturou a lepším výkonem.
| Kritérium | RecyclerView | ListView |
|---|---|---|
| ViewHolder | Povinný (architektura) | Doporučený, ale ne povinný |
| LayoutManager | Rozdělení: seznam, mřížka, kaskáda | Pouze vertikální seznam |
| Animace změn | DefaultItemAnimator ihned | Žádná vestavěná podpora |
| Oznamování změn | Bodové: notifyItemInserted/Removed | notifyDataSetChanged() pouze celý seznam |
| DiffUtil | Podpora přes DiffUtil.Callback | Žádný ekvivalent |
| Oddělovače | ItemDecoration (vlastní) | android:divider (vestavěný) |
| Minimální verze | API 14 (Android 4.0) přes AndroidX | API 1 (všechny verze) |
Doporučení Google (Android Developers, 2026): používejte RecyclerView pro všechny nové projekty. ListView lze ponechat ve starých kódových základnách (legacy), kde migrace neodůvodňuje náklady. RecyclerView poskytuje lepší výkon na 100+ prvcích, podporuje horizontální posouvání a mřížky bez dodatečných řešení. V Android 15 je ListView oficiálně označen jako částečně zastaralý.
Často kladené otázky
Zkontrolujte tři podmínky: (1) Adapter nastaven přes recyclerView.adapter = adapter, (2) LayoutManager nastaven přes recyclerView.layoutManager = LinearLayoutManager(context), (3) data v seznamu nejsou prázdná a adapter byl upozorněn (notifyDataSetChanged()). Typická chyba — nastavení dat po volání notifyDataSetChanged(). Data by měla být přiřazena před oznámením. Zkontrolujte velikost seznamu: adapter.itemCount by měl být větší než 0.
Zpomalení při posouvání (jank) vzniká, když onBindViewHolder() provádí těžké operace: načítání obrázků bez mezipaměti, findViewById() uvnitř bind, složité výpočty. Řešení: (1) použijte knihovnu Glide nebo Coil pro asynchronní načítání obrázků, (2) přesuňte všechny findViewById() do konstruktoru ViewHolder, (3) pro diferenční výpočty použijte AsyncListDiffer. Zapněte profilování GPU v Android Studio (Profile GPU Rendering) pro nalezení pomalých snímků.
Předejte orientaci LayoutManageru: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). Druhý parametr je reverseLayout (false = zleva doprava). Pro horizontální mřížku použijte GridLayoutManager s horizontální orientací nebo vlastní LinearSnapHelper pro stránkované posouvání. Horizontální seznamy jsou vhodné pro karusely produktů, obrázků, kategorií.
RecyclerView nemá vestavěný onClickListener (na rozdíl od ListView s onItemClickListener). Implementujte jej ve ViewHolder: předejte lambdu do konstruktoru a nastavte itemView.setOnClickListener. Příklad: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Volejte onItemClick(article) uvnitř bind(). Alternativa — nastavte RecyclerView.addOnItemTouchListener() pro zpracování gest.
RecycledViewPool — interní pool ViewHolderů, které se staly neviditelnými při posouvání. Místo zničení jsou uchovávány v poolu a znovu používány. Ve výchozím nastavení pool ukládá 5 ViewHolderů každého typu. Zvětšete velikost pro složitá rozvržení: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Pokud RecyclerView obsahuje více typů View (viewType), pool ukládá samostatné zásobníky pro každý. Sdílení RecycledViewPool mezi různými RecyclerView šetří paměť na obrazovkách s vnořenými seznamy.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také