LazyColumn — was ist das, effiziente Listen in Compose

Autor: IT Sectr Veröffentlicht: 2026-02-24 Lesezeit: 10 Min.

LazyColumn — eine Jetpack-Compose-Komponente zur Darstellung scrollbarer Listen mit träger Ladung. Im Gegensatz zu RecyclerView erstellt LazyColumn keine ViewHolder und verwendet kein XML — alle Elemente werden deklarativ durch composable-Funktionen beschrieben. Wir zeigen, was LazyColumn ist, wie LazyListState und die items-API funktionieren und wie Sie eine Leistung von 60 FPS erreichen. Bei IT Sectr verwenden wir LazyColumn in allen neuen Compose-Projekten. Zum Vergleich mit dem klassischen Ansatz lesen Sie den Artikel über RecyclerView.

Wichtige Punkte

  • LazyColumn — eine Jetpack-Compose-Komponente für vertikale Listen mit träger Ladung, Teil von Material Design 3.
  • LazyListState — Listenstatus: Bildlaufposition, sichtbare Elemente, programmgesteuertes Scrollen.
  • items API — Funktionen items(), itemsIndexed(), items() mit Schlüsseln zur Datenbindung.
  • key — Parameter zur stabilen Identifikation von Elementen, der es Compose ermöglicht, die Komposition wiederzuverwenden.
  • LazyVerticalGrid — Version von LazyColumn für Raster mit fester Spaltenanzahl.

Was ist LazyColumn in Jetpack Compose?

LazyColumn ist eine composable-Funktion aus der Foundation-Compose-Bibliothek, die eine vertikale Liste von Elementen mit träger Ladung anzeigt: Nur sichtbare oder nahe Elemente (Prefetch-Zone) werden erstellt und komponiert. LazyColumn erschien in Compose 1.0 (2021) zusammen mit LazyRow (horizontale Liste) und LazyVerticalGrid (Raster).

Träge Ladung funktioniert über den SubcomposeLayout-Mechanismus: LazyColumn misst den verfügbaren Platz, fordert den sichtbaren Bereich von LayoutInfo an und komponiert nur Elemente in diesem Bereich. Elemente, die aus dem Bildschirm scrollen, verlassen die Komposition (außer dem Prefetch-Puffer). Laut Google (Android Performance, 2026) hält LazyColumn 60 FPS beim Scrollen durch eine Liste von 10000+ Elementen auf Mittelklasse-Geräten.

Unterschied zu Column + ScrollState

Column mit vertikalem Scrollen ist eine normale Anordnung aller Elemente untereinander mit dem verticalScroll-Modifikator. Column ist nicht träge: Alle untergeordneten Elemente werden sofort komponiert, auch wenn sie nicht sichtbar sind. Bei einer Liste von 200+ Elementen verursacht Column eine erhebliche Verzögerung beim Start. LazyColumn komponiert nur sichtbare Elemente + Puffer (standardmäßig 1 Bildschirm vorwärts/rückwärts), was es für lange Listen unverzichtbar macht.

items API: Daten an die Liste binden

items API ist eine Reihe von Erweiterungsfunktionen für LazyColumn, die eine Datenliste und einen DSL-Deskriptor für jedes Element akzeptieren. Die Hauptfunktionen sind: items(count, key, itemContent) für eine feste Anzahl, items(list, key, itemContent) für eine Liste, itemsIndexed(list, key, itemContent) mit Index. Der key-Parameter ist für die Leistung obligatorisch — er gibt Compose eine stabile Elementkennung.

kotlin
// Basis-LazyColumn mit itemContent
@Composable
fun ArticleList(articles: List<Article>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Header — nicht träge, immer am Anfang
        item {
            Text("Neueste Artikel", style = MaterialTheme.typography.headlineMedium)
        }

        // Artikelliste mit Schlüssel nach id
        items(articles, key = { it.id }) { article ->
            ArticleCard(
                title = article.title,
                summary = article.summary,
                onClick = { onArticleClick(article.id) }
            )
        }

        // Footer mit Ladeanzeiger
        item {
            CircularProgressIndicator(
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            )
        }
    }
}

// Benutzerdefiniertes Listenelement
@Composable
fun ArticleCard(title: String, summary: String, onClick: () -> Unit) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
    ) {
        Column(modifier = Modifier.padding(12.dp)) {
            Text(text = title, style = MaterialTheme.typography.titleMedium)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = summary, style = MaterialTheme.typography.bodyMedium)
        }
    }
}

item {} vs items(): Verwenden Sie item {} für einzelne Elemente, die immer in der Liste vorhanden sind (Kopfzeile, Fußzeile, Trennzeichen). Verwenden Sie items() für eine dynamische Datenliste. items() akzeptiert ein Iterable oder Int und einen DSL-Block, der für jedes Element aufgerufen wird. Kombinieren Sie item und items in einem LazyColumn: Die Reihenfolge der Aufrufe bestimmt die Anzeigereihenfolge.

LazyListState: Verwaltung der Bildlaufposition

LazyListState ist ein Objekt, das den Listenstatus speichert: aktuelle Bildlaufposition (firstVisibleItemIndex, firstVisibleItemScrollOffset), sichtbare Elemente (layoutInfo) und Methoden zum programmgesteuerten Scrollen (scrollToItem, animateScrollToItem). LazyListState wird über rememberLazyListState() erstellt und über den state-Parameter an LazyColumn übergeben.

kotlin
// LazyColumn mit Beibehaltung der Bildlaufposition
@Composable
fun ScrollingList(items: List<Item>) {
    val listState = rememberLazyListState()

    Box(modifier = Modifier.fillMaxSize()) {
        LazyColumn(state = listState) {
            items(items, key = { it.id }) { item ->
                ListItemView(item = item)
            }
        }

        // Schaltfläche "Nach oben" erscheint nach dem 10. Element
        val showButton by remember {
            derivedStateOf { listState.firstVisibleItemIndex > 10 }
        }

        AnimatedVisibility(visible = showButton) {
            FloatingActionButton(
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .padding(16.dp),
                onClick = {
                    coroutineScope.launch {
                        listState.animateScrollToItem(0)
                    }
                }
            ) {
                Icon(Icons.Default.KeyboardArrowUp, "Nach oben")
            }
        }
    }
}

// Informationen zu sichtbaren Elementen abrufen
@Composable
fun ListDebugInfo(listState: LazyListState) {
    val visibleItems = listState.layoutInfo.visibleItemsInfo
    val totalItems = listState.layoutInfo.totalItemsCount
    val firstItem = visibleItems.firstOrNull()?.index
    val lastItem = visibleItems.lastOrNull()?.index

    Text("Zeige $firstItem–$lastItem von $totalItems")
}

Programmgesteuertes Scrollen erfolgt über listState-suspend-Funktionen: scrollToItem(index, scrollOffset) — sofortige Bewegung (ohne Animation), animateScrollToItem(index) — mit Animation. Zum Scrollen von einer Koroutine aus verwenden Sie LaunchedEffect oder coroutineScope.launch. Wichtig: Rufen Sie keine suspend-Funktionen aus der Komposition auf (innerhalb von @Composable) — verwenden Sie Lambdas (onClick, LaunchedEffect).

LazyColumn vs RecyclerView: Ansatzvergleich

LazyColumn und RecyclerView lösen dasselbe Problem — effiziente Anzeige langer Listen. Der Unterschied liegt in der Architektur: RecyclerView verwendet ViewHolder und Adapter (klassische Android-Ansicht), LazyColumn verwendet deklarative composable-Funktionen ohne XML oder ViewHolder. Die Wahl hängt vom Technologie-Stack des Projekts ab: Compose vs View-basierte UI.

ParameterLazyColumn (Compose)RecyclerView (View)
LayoutComposable-Funktionen (Kotlin DSL)XML-Dateien + ViewBinding
Adapteritems() / itemsIndexed() DSLRecyclerView.Adapter + ViewHolder
Sortieren/FilternSnapshot state + derivedStateOfDiffUtil + AsyncListDiffer
AnimationAnimatedVisibility + Modifier.animateItemDefaultItemAnimator
Verschiedene Elementtypenitem {}/items() + when nach TypgetItemViewType + mehrere ViewHolder
Zur Position scrollenlistState.animateScrollToItem()layoutManager.scrollToPosition()
PrefetchIn-process (SubcomposeLayout-Puffer)RecycledViewPool + GapWorker
Netzwerk/PaginierungcollectAsLazyPagingItems() + Paging 3PagingDataAdapter + Paging 3

Googles Empfehlung (Android Developers, 2026): Verwenden Sie für neue Projekte auf Jetpack Compose LazyColumn. Für View-basierte Projekte verwenden Sie RecyclerView. LazyColumn erfordert Compose, das etwa 3–5 MB zur APK hinzufügt — berücksichtigen Sie dies bei der Unterstützung älterer Geräte. Wenn ein Projekt bereits das View-System verwendet, kann LazyColumn nicht ohne AndroidView auf einem Bildschirm mit XML-Layout koexistieren — in solchen Fällen ist es einfacher, bei RecyclerView zu bleiben.

LazyVerticalGrid und LazyHorizontalGrid: Raster

LazyVerticalGrid ist eine Version von LazyColumn für Raster mit fester Spaltenanzahl. LazyHorizontalGrid ist ein horizontales Raster mit festen Zeilen. Beide Komponenten verwenden dieselben Prinzipien der trägen Ladung und dieselbe items-API wie LazyColumn. Der Parameter columns bestimmt die Anzahl der Spalten über GridCells.Fixed(N) oder GridCells.Adaptive(minSize).

kotlin
// Raster mit adaptiven Spalten
@Composable
fun PhotoGrid(photos: List<Photo>) {
    LazyVerticalGrid(
        columns = GridCells.Adaptive(minSize = 128.dp),
        contentPadding = PaddingValues(8.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(photos, key = { it.id }) { photo ->
            AsyncImage(
                model = photo.url,
                contentDescription = photo.title,
                modifier = Modifier.aspectRatio(1f)
            )
        }
    }
}

// Horizontale Liste (LazyRow)
@Composable
fun CategoryCarousel(categories: List<Category>) {
    LazyRow(
        contentPadding = PaddingValues(horizontal = 16.dp),
        horizontalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        items(categories, key = { it.id }) { category ->
            FilterChip(
                selected = category.isSelected,
                onClick = { onCategoryClick(category.id) },
                label = { Text(category.name) }
            )
        }
    }
}

GridCells.Adaptive berechnet automatisch die Anzahl der Spalten basierend auf der minimalen Zellgröße. Zum Beispiel platziert GridCells.Adaptive(128.dp) auf einem 360 dp breiten Bildschirm 2 Spalten, auf einem 600 dp Tablet — 4 Spalten. GridCells.Fixed(2) zeigt immer genau 2 Spalten. Adaptive ist für die Multi-Screen-Anpassung zu bevorzugen — es passt sich automatisch an die Bildschirmbreite ohne Media Queries an.

LazyColumn-Leistung: key, contentType, prefetch

Die Leistung von LazyColumn hängt von drei Faktoren ab: Schlüsselstabilität (key), Inhaltstyp (contentType) und Prefetch-Puffer (beyondBounds). Standardmäßig puffert LazyColumn 1 Bildschirm vorwärts und 1 rückwärts. Google (Android Developers, 2026) empfiehlt, diese Parameter für Listen mit unterschiedlichen Elementtypen oder komplexer Komposition zu konfigurieren.

ParameterBeschreibungEmpfehlung
keyStabile ElementkennungGeben Sie immer key = { it.id } an. Ohne key verwendet Compose den Index, und die Neuanordnung von Elementen zerstört die Animation.
contentTypeInhaltstyp zur Pool-TrennungGeben Sie dies für Listen mit unterschiedlichen Elementtypen (Text + Bild + Anzeige) an. Compose wird die Komposition nur innerhalb desselben contentType wiederverwenden.
beyondBoundsAnzahl der Prefetch-BildschirmeErhöhen Sie auf 2–3 für Listen mit schweren Bildern. Standard ist 1 Bildschirm.
Modifier.animateItemAnimationsänderung der PositionVerwenden Sie für animiertes Sortieren und Filtern (Compose 1.7+).
kotlin
// Optimierte LazyColumn mit contentType und prefetch
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
    val listState = rememberLazyListState()

    LazyColumn(
        state = listState,
        modifier = Modifier.fillMaxSize(),
        beyondBoundsPageCount = 2  // 2 Bildschirme prefetch
    ) {
        items(
            items = items,
            key = { it.id },
            contentType = { it.type }  // Aufteilung nach Typ
        ) { item ->
            when (item) {
                is FeedItem.Post -> PostView(item)
                is FeedItem.Ad -> AdView(item)
                is FeedItem.Suggested -> SuggestedView(item)
            }
        }
    }
}

// Leistungsmessung über listState
@Composable
fun ScrollPerformance(listState: LazyListState) {
    val scrollInfo = listState.layoutInfo
    val visibleCount = scrollInfo.visibleItemsInfo.size
    val total = scrollInfo.totalItemsCount

    // derivedStateOf — komponiert nicht neu, wenn sich der Wert nicht geändert hat
    val scrollProgress by remember {
        derivedStateOf {
            if (total > 0) visibleCount.toFloat() / total else 0f
        }
    }

    LinearProgressIndicator(
        progress = scrollProgress,
        modifier = Modifier.fillMaxWidth().height(2.dp)
    )
}

Häufige Fehler: (1) Fehlender key — Elemente werden bei jeder Listenänderung neu komponiert; (2) Verwendung von mutableStateListOf ohne Snapshot-Flows — Änderungen werden möglicherweise nicht verfolgt; (3) Aufruf von suspend-Funktionen innerhalb von itemContent — unterbricht die Komposition; (4) Kein contentType für gemischte Typen — Compose verwendet die Anzeigenkomposition für Textbeiträge wieder und verursacht visuelle Artefakte. Bei IT Sectr fügen wir contentType zu allen Listen mit drei oder mehr Inhaltstypen hinzu.

Häufig gestellte Fragen

LazyColumn scrollt langsam — was tun?

Überprüfen Sie drei Parameter: (1) key — ohne einen stabilen Schlüssel kann Compose die Komposition nicht wiederverwenden, (2) contentType — für Listen mit unterschiedlichen Elementtypen, (3) beyondBoundsPageCount — auf 2 für Bilder erhöhen. Verlagern Sie schwere Berechnungen innerhalb von itemContent in remember mit einem Schlüssel nach Daten. Verwenden Sie Modifier.drawWithContent anstelle von Image für einfache Grafiken.

Wie fügt man einen Trenner zwischen LazyColumn-Elementen hinzu?

Verwenden Sie LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) für Abstände zwischen Elementen. Für einen visuellen Trenner mit Linie fügen Sie item { Divider() } zwischen items hinzu. Automatischer Trenner: items(items, key = { it.id }) { item -> ... }, und zwischen jedem Element item { HorizontalDivider() } einfügen. Verwenden Sie dazu LazyListScope.items(list, key) { /* element */ } und separat LazyListScope.item { Divider() }.

LazyColumn innerhalb von Column funktioniert nicht — warum?

LazyColumn hat standardmäßig eine unendliche Höhe (fillMaxSize). Innerhalb einer Column ohne feste Höhe kann LazyColumn seine Größe nicht bestimmen. Lösung: (1) Legen Sie eine feste Höhe für LazyColumn fest: Modifier.height(400.dp), (2) verwenden Sie Modifier.weight(1f) innerhalb von Column, (3) verschachteln Sie LazyColumn nicht in einem vertikal scrollbaren Container — verwenden Sie LazyColumn als Wurzelelement mit item {} für Kopf- und Fußzeile.

Wie aktualisiert man LazyColumn bei Datenänderungen?

LazyColumn reagiert automatisch auf State-Änderungen. Wenn Daten in mutableStateListOf oder mutableStateOf gespeichert sind, komponiert Compose nur die geänderten Elemente neu. Für Listen aus ViewModel verwenden Sie collectAsState() mit Flow. Beim Aktualisieren der Liste mit key animiert Compose Änderungen (Hinzufügen, Entfernen, Neuanordnen) automatisch über Modifier.animateItemPlacement().

LazyColumn vs LazyVerticalGrid — was wählen?

LazyColumn — für einspaltige Listen (Feed, Chat, Kommentare). LazyVerticalGrid — für Raster (Galerie, Katalog, Symbole). Wenn Elemente auf dem Telefon in einer Spalte und auf dem Tablet in zwei Spalten sein sollen — verwenden Sie LazyVerticalGrid mit GridCells.Adaptive. Wenn ein gemischtes Raster benötigt wird (unterschiedliche Spaltenanzahl in einem Abschnitt) — verwenden Sie LazyColumn und fügen Sie horizontales LazyRow oder verschachteltes LazyVerticalGrid innerhalb von items ein.

Zusammenfassung

  • LazyColumn — eine Jetpack-Compose-Komponente für träge vertikale Listen mit deklarativer API.
  • items() API akzeptiert eine Liste und einen DSL-Block für jedes Element; key ist für die Leistung obligatorisch.
  • LazyListState verwaltet die Bildlaufposition und bietet suspend-Funktionen für programmgesteuertes Scrollen.
  • contentType trennt Kompositionspools für Listen mit unterschiedlichen Elementtypen.
  • LazyVerticalGrid / LazyRow — Varianten für Raster und horizontale Listen mit derselben items-API.
  • LazyColumn hält 60 FPS für 10000+ Elemente dank SubcomposeLayout und Prefetch-Puffer.
  • Für neue Compose-Projekte verwenden Sie LazyColumn; für View-basierte Projekte — RecyclerView.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch