ListView — basis Android-component voor het weergeven van verticale lijsten, beschikbaar sinds API 1 (Android 1.0). ListView maakt een lijst van elementen met behulp van ArrayAdapter en ondersteunt aangepaste celindelingen. Ontdek wat ListView is, hoe je het instelt en welke valkuilen deze oude maar nog steeds gebruikte component verbergt. Bij IT Sectr komen we ListView tegen in legacy-projecten en onderhouden we achterwaartse compatibiliteit met Android 4.4. Voor een vergelijking met het moderne alternatief lees het artikel over RecyclerView.
Belangrijkste
ListView — is een View-groep uit het package android.widget, ontworpen voor het weergeven van een scrollbare lijst van elementen. ListView implementeert het AdapterView-patroon: gegevens worden via een adapter (Adapter) doorgegeven die gegevenselementen omzet in View die op het scherm worden weergegeven. ListView bestaat in Android SDK sinds de eerste versie (API 1) en vereist geen extra bibliotheken.
Architectuur van ListView is eenvoudiger dan RecyclerView: LayoutManager is ingebouwd (alleen verticaal scrollen), animatie van wijzigingen ontbreekt, scheidingstekens worden ingesteld via het XML-attribuut android:divider. ListView voegt automatisch OverScrollView en markering bij focus toe, wat prototypen vereenvoudigt. Volgens Statista (2025) bevat ongeveer 15% van de Android-apps in de top-100 van Play Store nog steeds ListView — voornamelijk legacy-projecten met minimale ondersteuning.
ListView was van 2008 tot 2014 de belangrijkste lijstcomponent in Android, tot op Google I/O 2014 RecyclerView werd geïntroduceerd. Ondanks de status van gedeeltelijk verouderd (soft-deprecated), blijft ListView beschikbaar in alle Android-versies en is het niet gemarkeerd als @Deprecated in SDK API 35. Als uw project Android 4.4 (API 19) en lager ondersteunt, is ListView de enige ingebouwde keuze, omdat RecyclerView AndroidX en Gradle-afhankelijkheden vereist.
ArrayAdapter — de populairste adapter voor ListView. Het accepteert List<T> en een indeling voor elk element. Standaard roept ArrayAdapter toString() aan voor elk object en geeft de tekst weer in simple_list_item_1 (één TextView). Voor aangepaste weergave moet getView() worden overschreven of een aangepaste indeling worden doorgegeven in de constructor.
// ListView met ArrayAdapter (eenvoudige tekst)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Rusland", "VS", "Duitsland",
"Frankrijk", "Japan", "China", "Brazilië"
)
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
// Klikafhandeling
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter is bedoeld voor lijsten uit Map-gegevens. Het accepteert List<Map<String, Any>> en een from/to-array voor het koppelen van Map-sleutels aan ID's van widgets in de indeling. SimpleAdapter is handig voor snelle prototypes wanneer geen aparte adapterklasse nodig is. Nadeel — geen type-safe typering en complexiteit met aangepaste logica.
<!-- Indeling met 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>
Aangepaste adapter voor ListView overschrijft de methode getView(), die RecyclerView opsplitst in onCreateViewHolder + onBindViewHolder. In ListView zijn beide stappen gecombineerd, dus de ontwikkelaar moet het ViewHolder-patroon zelf implementeren via convertView (hergebruikte View) en tag setTag().
// Gegevensmodel
data class Product(
name: String,
price: String,
imageResId: Int
)
// Aangepaste adapter met ViewHolder
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// ViewHolder voor caching
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
}
}
Belangrijk verschil met RecyclerView: in ListView wordt de methode getView() aangeroepen bij elke verschijning van een element op het scherm. Als convertView en ViewHolder niet worden gebruikt, maakt elke scroll een nieuw View-object aan, wat leidt tot geheugenlekken en vertraging op lijsten met 50+ elementen. ViewHolder met view.tag — verplichte optimalisatie voor ListView met aangepaste indelingen.
ListView heeft vier architecturale beperkingen die Google in RecyclerView heeft opgelost: (1) alleen verticale oriëntatie — geen raster, horizontale lijsten of cascades; (2) geen puntmeldingen — elke gegevenswijziging veroorzaakt volledig hertekenen via notifyDataSetChanged(), wat schokkerigheid veroorzaakt; (3) geen ingebouwde animatie voor toevoegen/verwijderen van elementen; (4) geen scheiding van verantwoordelijkheden — LayoutManager is ingebouwd, ItemAnimator ontbreekt.
<!-- item_product.xml — indeling van ListView-element -->
<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>
Praktische gevolgen: op lijsten tot 20 elementen werkt ListView net zo snel als RecyclerView. Bij 100+ elementen wordt het verschil merkbaar: ListView begint FPS te verliezen door het ontbreken van differentiële berekeningen en gedwongen ViewHolder. Voor lijsten met frequente updates (chat, feed, logs) is ListView niet geschikt — gebruik RecyclerView met Paging 3. Bij IT Sectr migreren we ListView naar RecyclerView bij elk werk aan legacy-schermen met meer dan 50 elementen.
ListView biedt een set XML-attributen voor snelle configuratie zonder code: android:divider (kleur/tekening van scheidingsteken), android:dividerHeight (dikte), android:scrollbars (scrollbalken), android:fastScrollEnabled (snel scrollen via handgreep), android:choiceMode (selectiemodus: singleChoice, multipleChoice). Het attribuut android:entries maakt het mogelijk de lijst te vullen vanuit een bronnenarray (string-array) zonder adapter.
| Attribuut | Beschrijving | Voorbeeldwaarde |
|---|---|---|
| android:divider | Kleur of drawable van scheidingsteken | @color/gray_light |
| android:dividerHeight | Dikte van scheidingsteken in px/dp | 1dp |
| android:entries | Array van strings uit bronnen | @array/countries |
| android:choiceMode | Selectiemodus van element | singleChoice |
| android:fastScrollEnabled | Snel scrollen | true |
| android:overlapAnchor | Anker voor uitklapbare lijsten (API 21+) | true |
Selectiemodus: choiceMode="singleChoice" activeert RadioButton-stijl, waarbij het geselecteerde element wordt gemarkeerd. choiceMode="multipleChoice" — selectievakjes voor meerdere elementen. Selectiegegevens zijn toegankelijk via listView.checkedItemPosition of listView.checkedItemIds. Belangrijk: bij choiceMode gebruikt ListView StateListDrawable voor de achtergrond — stel een eigen selector in als het standaard blauw niet bij het ontwerp past.
Keuze tussen ListView en RecyclerView hangt af van de projectvereisten. RecyclerView is de moderne standaard, maar ListView kan gerechtvaardigd zijn voor eenvoudige schermen met minimale onderhoudskosten van oude code. Vergelijking op alle belangrijke parameters:
| Parameter | ListView | RecyclerView |
|---|---|---|
| Bibliotheekgrootte | Ingebouwd in SDK (≈60 KB) | AndroidX (≈300 KB met afhankelijkheden) |
| Minimale versie | API 1 | API 14 (via AndroidX) |
| Orientatie | Alleen verticaal | Verticaal, horizontaal, raster, cascade |
| ViewHolder | Handmatige implementatie | Verplicht door architectuur |
| Animatie | Alleen fade-overgangen | DefaultItemAnimator (add, remove, move, change) |
| Puntupdates | Nee (alleen notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Click listener | onItemClickListener (ingebouwd) | Via ViewHolder (geen ingebouwde) |
| Prestaties | Voldoende voor lijsten tot 50 elementen | Stabiele 60 FPS voor 1000+ elementen |
Conclusie: gebruik RecyclerView voor nieuwe projecten. Laat ListView alleen in legacy-code waar migratie onredelijk duur is. Volgens de richtlijnen van Material Design 3 (2026) moeten lijsten van elke complexiteit RecyclerView gebruiken om te voldoen aan moderne prestatie- en toegankelijkheidsnormen.
Veelgestelde vragen
ListView is niet gemarkeerd als @Deprecated in Android SDK API 35, maar Google raadt het gebruik ervan in nieuwe projecten af (soft-deprecated). De component blijft functioneel in alle Android-versies. RecyclerView is sinds 2014 de officiële vervanger. Als u een nieuw project start — kies RecyclerView. Als u een bestaand project onderhoudt — blijft ListView zonder wijzigingen werken.
ListView ondersteunt de methoden addHeaderView(View, Object, boolean) en addFooterView(View, Object, boolean). Header en footer worden toegevoegd aan de algemene lijst van elementen, maar doen niet mee in de adapter — ze worden altijd aan het begin en einde van de lijst weergegeven. Belangrijk: voeg header/footer toe vóór het instellen van de adapter (setAdapter()), anders worden ze niet meegenomen. Na toevoeging van een header verschuiven de ID's van elementposities met het aantal headers.
ListView en ScrollView verwerken beide verticale gebaren, wat een conflict veroorzaakt: de externe ScrollView onderschept het scrollen en ListView scrollt niet. Oplossing: (1) plaats ListView niet in ScrollView — gebruik RecyclerView met NestedScrollingChild, (2) vervang ScrollView door NestedScrollView (API 21+), (3) gebruik ListView met vaste hoogte (android:layout_height="...dp") binnen ScrollView, maar dit breekt de aanpasbaarheid.
Gegevens wissen in ListView hangt af van het type adapter. Voor ArrayAdapter: adapter.clear() en adapter.addAll(newList) gevolgd door adapter.notifyDataSetChanged(). Voor aangepaste adapter: werk de gegevenslijst bij en roep notifyDataSetChanged() aan. Let op: listView.invalidateViews() tekent alleen View opnieuw, maar werkt geen gegevens bij. Wijzig altijd gegevens via de adapter, niet direct.
De hoogte van een ListView-element wordt ingesteld in de item-indeling. Als de indeling layout_height="wrap_content" gebruikt, past de hoogte zich aan de inhoud aan. Voor vaste hoogte van alle elementen gebruikt u een aangepaste adapter die LayoutParams instelt in getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Voor verschillende hoogtes gebruikt u RecyclerView met verschillende weergavetypes.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook