ListView — grundläggande Android-komponent för att visa vertikala listor, tillgänglig från API 1 (Android 1.0). ListView skapar en lista av element med hjälp av ArrayAdapter och stöder anpassade cellayouter. Lär dig vad ListView är, hur du konfigurerar det och vilka fallgropar denna gamla men fortfarande använda komponent döljer. På IT Sectr stöter vi på ListView i legacy-projekt och upprätthåller bakåtkompatibilitet med Android 4.4. För jämförelse med det moderna alternativet läs artikeln om RecyclerView.
Huvudpunkter
ListView — är en View-grupp från paketet android.widget, avsedd för att visa en rullningsbar lista av element. ListView implementerar AdapterView-mönstret: data skickas via en adapter (Adapter) som konverterar dataelement till View som visas på skärmen. ListView har funnits i Android SDK sedan första versionen (API 1) och kräver inga extra bibliotek.
Arkitektur för ListView är enklare än RecyclerView: LayoutManager är inbyggd (endast vertikal rullning), animering av ändringar saknas, avgränsare ställs in via XML-attributet android:divider. ListView lägger automatiskt till OverScrollView och markering vid fokus, vilket förenklar prototypframställning. Enligt Statista (2025) innehåller cirka 15% av Android-apparna i Play Store topp-100 fortfarande ListView — främst legacy-projekt med minimalt stöd.
ListView var den huvudsakliga listkomponenten i Android från 2008 till 2014, när RecyclerView presenterades på Google I/O 2014. Trots statusen som delvis föråldrad (soft-deprecated) förblir ListView tillgänglig i alla Android-versioner och är inte markerad som @Deprecated i SDK API 35. Om ditt projekt stöder Android 4.4 (API 19) och lägre är ListView det enda inbyggda valet, eftersom RecyclerView kräver AndroidX och gradle-beroenden.
ArrayAdapter — den mest populära adaptern för ListView. Den accepterar List<T> och en layout för varje element. Som standard anropar ArrayAdapter toString() för varje objekt och visar texten i simple_list_item_1 (en TextView). För anpassad visning måste getView() åsidosättas eller en anpassad layout skickas i konstruktorn.
// ListView med ArrayAdapter (enkel text)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Ryssland", "USA", "Tyskland",
"Frankrike", "Japan", "Kina", "Brasilien"
)
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
// Klickhantering
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter är avsedd för listor från Map-data. Den accepterar List<Map<String, Any>> och en from/to-matris för att binda Map-nycklar med ID:n för widgetar i layouten. SimpleAdapter är bekväm för snabba prototyper när ingen separat adapterklass behövs. Nackdel — brist på type-safe-typning och komplexitet med anpassad logik.
<!-- Layout med 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>
Anpassad adapter för ListView åsidosätter metoden getView(), som RecyclerView delar upp i onCreateViewHolder + onBindViewHolder. I ListView är båda stegen kombinerade, så utvecklaren måste implementera ViewHolder-mönstret självständigt via convertView (återanvänd View) och tag setTag().
// Datamodell
data class Product(
name: String,
price: String,
imageResId: Int
)
// Anpassad adapter med ViewHolder
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// ViewHolder för cachning
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
}
}
Viktig skillnad från RecyclerView: i ListView anropas metoden getView() för varje gång ett element visas på skärmen. Om convertView och ViewHolder inte används skapar varje rullning ett nytt View-objekt, vilket leder till minnesläckor och fördröjning på listor med 50+ element. ViewHolder med view.tag — obligatorisk optimering för ListView med anpassade layouter.
ListView har fyra arkitektoniska begränsningar som Google åtgärdade i RecyclerView: (1) endast vertikal orientering — inga rutnät, horisontella listor eller kaskader; (2) avsaknad av punktmeddelanden — varje dataändring orsakar fullständig omritning via notifyDataSetChanged(), vilket skapar ryckighet; (3) ingen inbyggd animering för tillägg/borttagning av element; (4) ingen separation av ansvarsområden — LayoutManager är inbyggd, ItemAnimator saknas.
<!-- item_product.xml — layout för 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>
Praktiska konsekvenser: på listor upp till 20 element fungerar ListView lika snabbt som RecyclerView. Vid 100+ element blir skillnaden märkbar: ListView börjar tappa FPS på grund av brist på differentiella beräkningar och tvingad ViewHolder. För listor med frekventa uppdateringar (chatt, feed, loggar) är ListView inte lämplig — använd RecyclerView med Paging 3. På IT Sectr migrerar vi ListView till RecyclerView vid varje arbete på legacy-skärmar med mer än 50 element.
ListView tillhandahåller en uppsättning XML-attribut för snabb konfiguration utan kod: android:divider (färg/bild på avgränsare), android:dividerHeight (tjocklek), android:scrollbars (rullningslister), android:fastScrollEnabled (snabb rullning via handtag), android:choiceMode (markeringsläge: singleChoice, multipleChoice). Attributet android:entries gör det möjligt att fylla listan från en resursmatris (string-array) utan adapter.
| Attribut | Beskrivning | Exempelvärde |
|---|---|---|
| android:divider | Färg eller drawable för avgränsare | @color/gray_light |
| android:dividerHeight | Tjocklek på avgränsare i px/dp | 1dp |
| android:entries | Matris med strängar från resurser | @array/countries |
| android:choiceMode | Markeringsläge för element | singleChoice |
| android:fastScrollEnabled | Snabb rullning | true |
| android:overlapAnchor | Ankare för popup-listor (API 21+) | true |
Markeringsläge: choiceMode="singleChoice" aktiverar RadioButton-stil, där det markerade elementet markeras. choiceMode="multipleChoice" — kryssrutor för flera element. Markeringsdata är tillgänglig via listView.checkedItemPosition eller listView.checkedItemIds. Viktigt: vid choiceMode använder ListView StateListDrawable för bakgrunden — ställ in en egen väljare om standardblått inte passar designen.
Val mellan ListView och RecyclerView beror på projektets krav. RecyclerView är den moderna standarden, men ListView kan vara motiverad för enkla skärmar med minimala underhållskostnader för gammal kod. Jämförelse baserat på alla nyckelparametrar:
| Parameter | ListView | RecyclerView |
|---|---|---|
| Biblioteksstorlek | Inbyggd i SDK (≈60 KB) | AndroidX (≈300 KB med beroenden) |
| Minsta version | API 1 | API 14 (via AndroidX) |
| Orientering | Endast vertikal | Vertikal, horisontell, rutnät, kaskad |
| ViewHolder | Manuell implementering | Obligatorisk enligt arkitektur |
| Animering | Endast fade-övergångar | DefaultItemAnimator (add, remove, move, change) |
| Punktuppdateringar | Nej (endast notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Click listener | onItemClickListener (inbyggd) | Via ViewHolder (ingen inbyggd) |
| Prestanda | Tillräcklig för listor upp till 50 element | Stabila 60 FPS för 1000+ element |
Slutsats: för nya projekt använd RecyclerView. Lämna ListView endast i legacy-kod där migrering är orimligt dyr. Enligt riktlinjerna för Material Design 3 (2026) bör listor av vilken komplexitet som helst använda RecyclerView för att uppfylla moderna prestanda- och tillgänglighetsstandarder.
Vanliga frågor
ListView är inte markerat som @Deprecated i Android SDK API 35, men Google rekommenderar inte att det används i nya projekt (soft-deprecated). Komponenten förblir funktionell i alla Android-versioner. RecyclerView är den officiella ersättaren sedan 2014. Om du startar ett nytt projekt — välj RecyclerView. Om du underhåller ett befintligt — kommer ListView att fortsätta fungera utan ändringar.
ListView stöder metoderna addHeaderView(View, Object, boolean) och addFooterView(View, Object, boolean). Header och footer läggs till i den allmänna listan av element, men deltar inte i adaptern — de visas alltid i början och slutet av listan. Viktigt: lägg till header/footer innan du ställer in adaptern (setAdapter()), annars kommer de inte att beaktas. Efter att ha lagt till header förskjuts ID:n för elementpositioner med antalet headers.
ListView och ScrollView hanterar båda vertikala gester, vilket skapar en konflikt: den yttre ScrollView fångar upp rullningen och ListView rullar inte. Lösning: (1) lägg inte ListView i ScrollView — använd RecyclerView med NestedScrollingChild, (2) ersätt ScrollView med NestedScrollView (API 21+), (3) använd ListView med fast höjd (android:layout_height="...dp") inuti ScrollView, men detta kommer att bryta anpassningsbarheten.
Rensning av data i ListView beror på adaptertypen. För ArrayAdapter: adapter.clear() och adapter.addAll(newList) följt av adapter.notifyDataSetChanged(). För anpassad adapter: uppdatera datalistan och anropa notifyDataSetChanged(). Observera: listView.invalidateViews() ritar bara om View, men uppdaterar inte data. Ändra alltid data via adaptern, inte direkt.
Höjden på ett ListView-element ställs in i item-layouten. Om layouten använder layout_height="wrap_content" anpassar sig höjden efter innehållet. För fast höjd på alla element använder du en anpassad adapter som ställer in LayoutParams i getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). För olika höjder använder du RecyclerView med olika vytyper.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också