ListView — základní komponent Androidu pro zobrazování vertikálních seznamů, dostupný od API 1 (Android 1.0). ListView vytváří seznam z prvků pomocí ArrayAdapter a podporuje vlastní rozvržení buněk. Zjistíme, co je ListView, jak ho nakonfigurovat a jaké nástrahy skrývá tento starý, ale stále používaný komponent. V IT Sectr se s ListView setkáváme v legacy projektech a udržujeme zpětnou kompatibilitu s Android 4.4. Pro srovnání s moderní alternativou si přečtěte článek o RecyclerView.
Hlavní body
ListView — je skupina View z balíčku android.widget, určená k zobrazování posouvatelného seznamu prvků. ListView implementuje vzor AdapterView: data se předávají přes adaptér (Adapter), který převádí datové prvky na View zobrazovaná na obrazovce. ListView existuje v Android SDK od první verze (API 1) a nevyžaduje další knihovny.
Architektura ListView je jednodušší než RecyclerView: LayoutManager je vestavěný (pouze vertikální posouvání), animace změn chybí, oddělovače se nastavují pomocí XML atributu android:divider. ListView automaticky přidává OverScrollView a zvýraznění při fokusu, což zjednodušuje prototypování. Podle údajů Statista (2025) asi 15% Android aplikací v top-100 Play Store stále obsahuje ListView — zejména legacy projekty s minimální podporou.
ListView byl hlavním komponentem seznamů v Androidu od roku 2008 do roku 2014, kdy byl na Google I/O 2014 představen RecyclerView. Navzdory statusu částečně zastaralého (soft-deprecated) zůstává ListView dostupný ve všech verzích Androidu a není označen jako @Deprecated v SDK API 35. Pokud váš projekt podporuje Android 4.4 (API 19) a nižší, je ListView jedinou vestavěnou volbou, protože RecyclerView vyžaduje AndroidX a gradle závislosti.
ArrayAdapter — nejpopulárnější adaptér pro ListView. Přijímá List<T> a rozvržení pro každý prvek. Ve výchozím nastavení ArrayAdapter volá toString() pro každý objekt a zobrazuje text v simple_list_item_1 (jeden TextView). Pro vlastní zobrazení je třeba přepsat getView() nebo předat vlastní rozvržení v konstruktoru.
// ListView s ArrayAdapter (jednoduchý text)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Rusko", "USA", "Německo",
"Francie", "Japonsko", "Čína", "Brazílie"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
countries
)
binding.listView.adapter = adapter
// Zpracování kliknutí
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter je určen pro seznamy z Map dat. Přijímá List<Map<String, Any>> a pole from/to pro propojení klíčů Map s ID widgetů v rozvržení. SimpleAdapter je vhodný pro rychlé prototypy, když není potřeba samostatná třída adaptéru. Nevýhoda — chybí type-safe typování a složitost s vlastní logikou.
<!-- Rozvržení s ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/divider"
android:dividerHeight="1px"
android:scrollbars="vertical" />
</LinearLayout>
Vlastní adaptér pro ListView přepisuje metodu getView(), kterou RecyclerView rozděluje na onCreateViewHolder + onBindViewHolder. V ListView jsou oba kroky sloučeny, proto musí vývojář implementovat vzor ViewHolder samostatně pomocí convertView (znovu použitelné View) a tagu setTag().
// Datový model
data class Product(
name: String,
price: String,
imageResId: Int
)
// Vlastní adaptér s ViewHolder
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// ViewHolder pro cachování
private class ViewHolder(view: View) {
val nameText: TextView = view.findViewById(R.id.tvName)
val priceText: TextView = view.findViewById(R.id.tvPrice)
val imageView: ImageView = view.findViewById(R.id.ivProduct)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(R.layout.item_product, parent, false)
.also { it.tag = ViewHolder(it) }
val holder = view.tag as ViewHolder
val product = getItem(position)
holder.nameText.text = product.name
holder.priceText.text = product.price
holder.imageView.setImageResource(product.imageResId)
return view
}
}
Klíčový rozdíl od RecyclerView: v ListView se metoda getView() volá při každém objevení prvku na obrazovce. Pokud se nepoužije convertView a ViewHolder, každé posouvání vytváří nový objekt View, což vede k únikům paměti a zpožděním na seznamech s 50+ prvky. ViewHolder s view.tag — povinná optimalizace pro ListView s vlastními rozvrženími.
ListView má čtyři architektonická omezení, která Google opravil v RecyclerView: (1) pouze vertikální orientace — žádné mřížky, horizontální seznamy nebo kaskády; (2) chybí bodová oznámení — jakákoli změna dat způsobuje úplné překreslení přes notifyDataSetChanged(), což vytváří trhání; (3) žádná vestavěná animace přidávání/odebírání prvků; (4) žádné oddělení odpovědností — LayoutManager je vestavěný, ItemAnimator chybí.
<!-- item_product.xml — rozvržení prvku ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:id="@+id/ivProduct"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="12dp">
<TextView
android:id="@+id/tvName"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPrice"
android:textSize="14sp"
android:textColor="@color/price" />
</LinearLayout>
</LinearLayout>
Praktické důsledky: na seznamech do 20 prvků funguje ListView stejně rychle jako RecyclerView. Při 100+ prvcích je rozdíl znatelný: ListView začíná ztrácet FPS kvůli chybějícím diferenčním výpočtům a vynucenému ViewHolder. Pro seznamy s častými aktualizacemi (chat, feed, logy) není ListView vhodný — použijte RecyclerView s Paging 3. V IT Sectr migrujeme ListView na RecyclerView při každé práci na legacy obrazovkách s více než 50 prvky.
ListView poskytuje sadu XML atributů pro rychlou konfiguraci bez kódu: android:divider (barva/obrázek oddělovače), android:dividerHeight (tloušťka), android:scrollbars (posuvníky), android:fastScrollEnabled (rychlé posouvání pomocí úchytu), android:choiceMode (režim výběru: singleChoice, multipleChoice). Atribut android:entries umožňuje naplnit seznam z pole zdrojů (string-array) bez adaptéru.
| Atribut | Popis | Příklad hodnoty |
|---|---|---|
| android:divider | Barva nebo drawable oddělovače | @color/gray_light |
| android:dividerHeight | Tloušťka oddělovače v px/dp | 1dp |
| android:entries | Pole řetězců ze zdrojů | @array/countries |
| android:choiceMode | Režim výběru prvku | singleChoice |
| android:fastScrollEnabled | Rychlé posouvání | true |
| android:overlapAnchor | Kotva pro rozbalovací seznamy (API 21+) | true |
Režim výběru: choiceMode="singleChoice" aktivuje styl RadioButton, při kterém je vybraný prvek zvýrazněn. choiceMode="multipleChoice" — zaškrtávací políčka pro více prvků. Data výběru jsou dostupná přes listView.checkedItemPosition nebo listView.checkedItemIds. Důležité: při choiceMode používá ListView StateListDrawable pro pozadí — nastavte vlastní selektor, pokud standardní modrá nevyhovuje designu.
Výběr mezi ListView a RecyclerView závisí na požadavcích projektu. RecyclerView je moderní standard, ale ListView může být ospravedlnitelný pro jednoduché obrazovky s minimálními náklady na údržbu starého kódu. Srovnání podle všech klíčových parametrů:
| Parametr | ListView | RecyclerView |
|---|---|---|
| Velikost knihovny | Vestavěný v SDK (≈60 KB) | AndroidX (≈300 KB se závislostmi) |
| Minimální verze | API 1 | API 14 (přes AndroidX) |
| Orientace | Pouze vertikální | Vertikální, horizontální, mřížka, kaskáda |
| ViewHolder | Ruční implementace | Povinný architekturou |
| Animace | Pouze fade přechody | DefaultItemAnimator (add, remove, move, change) |
| Bodové aktualizace | Ne (pouze notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Click listener | onItemClickListener (vestavěný) | Přes ViewHolder (není vestavěný) |
| Výkon | Dostačující pro seznamy do 50 prvků | Stabilních 60 FPS pro 1000+ prvků |
Závěr: pro nové projekty používejte RecyclerView. ListView ponechte pouze v legacy kódu, kde je migrace neospravedlnitelně drahá. Podle pokynů Material Design 3 (2026) by seznamy jakékoli složitosti měly používat RecyclerView pro soulad s moderními standardy výkonu a přístupnosti.
Často kladené otázky
ListView není označen jako @Deprecated v Android SDK API 35, ale Google nedoporučuje jeho použití v nových projektech (soft-deprecated). Komponent zůstává funkční ve všech verzích Androidu. RecyclerView je oficiální náhradou od roku 2014. Pokud začínáte nový projekt — zvolte RecyclerView. Pokud udržujete stávající — ListView bude nadále fungovat bez změn.
ListView podporuje metody addHeaderView(View, Object, boolean) a addFooterView(View, Object, boolean). Header a footer se přidávají do obecného seznamu prvků, ale neúčastní se adaptéru — vždy se zobrazují na začátku a konci seznamu. Důležité: přidávejte header/footer před nastavením adaptéru (setAdapter()), jinak nebudou zohledněny. Po přidání header se ID pozic prvků posunou o počet headerů.
ListView a ScrollView oba zpracovávají vertikální gesta, což vytváří konflikt: vnější ScrollView zachytává posouvání a ListView se neposouvá. Řešení: (1) nevkládejte ListView do ScrollView — použijte RecyclerView s NestedScrollingChild, (2) nahraďte ScrollView za NestedScrollView (API 21+), (3) použijte ListView s pevnou výškou (android:layout_height="...dp") uvnitř ScrollView, ale to zlomí přizpůsobivost.
Čištění dat v ListView závisí na typu adaptéru. Pro ArrayAdapter: adapter.clear() a adapter.addAll(newList) s následným adapter.notifyDataSetChanged(). Pro vlastní adaptér: aktualizujte seznam dat a zavolejte notifyDataSetChanged(). Pozor: listView.invalidateViews() pouze překresluje View, ale neaktualizuje data. Vždy měňte data přes adaptér, ne přímo.
Výška prvku ListView se nastavuje v rozvržení item. Pokud rozvržení používá layout_height="wrap_content", výška se přizpůsobí obsahu. Pro pevnou výšku všech prvků použijte vlastní adaptér, který nastavuje LayoutParams v getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Pro různorodé výšky použijte RecyclerView s různými typy zobrazení.
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é