LazyColumn — vad är det, effektiva listor i Compose

Författare: IT Sectr Publicerad: 2026-02-24 Lästid: 10 min

LazyColumn — Jetpack Compose-komponent för att visa rullningsbara listor med lat lastning. Till skillnad från RecyclerView skapar LazyColumn inga ViewHolders och använder inte XML — alla element beskrivs deklarativt genom composable-funktioner. Vi visar vad LazyColumn är, hur LazyListState och items API fungerar, och hur man uppnår prestanda på 60 FPS. Hos IT Sectr använder vi LazyColumn i alla nya Compose-projekt. För jämförelse med det klassiska tillvägagångssättet, läs artikeln om RecyclerView.

Huvudpunkter

  • LazyColumn — Jetpack Compose-komponent för vertikala listor med lat lastning, en del av Material Design 3.
  • LazyListState — liststatus: rullningsposition, synliga element, programmerbar rullning.
  • items API — funktioner items(), itemsIndexed(), items() med nycklar för databindning.
  • key — parameter för stabil identifiering av element, vilket gör att Compose kan återanvända komposition.
  • LazyVerticalGrid — version av LazyColumn för rutnät med fast antal kolumner.

Vad är LazyColumn i Jetpack Compose?

LazyColumn — är en composable-funktion från Foundation Compose-biblioteket som visar en vertikal lista med element med lat (lazy) lastning: endast element som är synliga på skärmen eller i närheten (prefetch-zon) skapas och komponeras. LazyColumn dök upp i Compose 1.0 (2021) tillsammans med LazyRow (horisontell lista) och LazyVerticalGrid (rutnät).

Lat lastning fungerar genom mekanismen SubcomposeLayout: LazyColumn mäter tillgängligt utrymme, begär den synliga räckvidden från LayoutInfo och komponerar endast element i den räckvidden. Element som har lämnat skärmen lämnar kompositionen (förutom prefetch-bufferten). Enligt Google-data (Android Performance, 2026) ger LazyColumn 60 FPS vid rullning av en lista med 10000+ element på medelklass-enheter.

Skillnad mot Column + ScrollState

Column med vertikal rullning — är vanlig placering av alla element under varandra med tillämpning av verticalScroll-modifieraren. Column är inte lat: alla underordnade element komponeras omedelbart, även om de inte är synliga. För en lista med 200+ element orsakar Column betydande fördröjning vid start. LazyColumn komponerar endast synliga element + buffert (som standard 1 skärm framåt/bakåt), vilket gör den oumbärlig för långa listor.

items API: databindning till lista

items API — uppsättning tilläggsfunktioner för LazyColumn som accepterar en datalista och en DSL-beskrivning för varje element. Huvudfunktioner: items(count, key, itemContent) för fast antal, items(list, key, itemContent) för lista, itemsIndexed(list, key, itemContent) med index. Parametern key — obligatoriskt villkor för prestanda — ger Compose en stabil identifierare för elementet.

kotlin
// Grundläggande LazyColumn med itemContent
@Composable
fun ArticleList(articles: List<Article>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Header — inte lat, alltid i början
        item {
            Text("Senaste artiklarna", style = MaterialTheme.typography.headlineMedium)
        }

        // Lista över artiklar med nyckel på id
        items(articles, key = { it.id }) { article ->
            ArticleCard(
                title = article.title,
                summary = article.summary,
                onClick = { onArticleClick(article.id) }
            )
        }

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

// Anpassat listelement
@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(): använd item {} för enskilda element som alltid finns i listan (header, footer, avskiljare). Använd items() för dynamisk datalista. items() accepterar Iterable eller Int och ett DSL-block som anropas för varje element. Kombinera item och items i samma LazyColumn: anropsordningen bestämmer visningsordningen.

LazyListState: hantering av rullningsposition

LazyListState — objekt som lagrar liststatus: aktuell rullningsposition (firstVisibleItemIndex, firstVisibleItemScrollOffset), synliga element (layoutInfo) och metoder för programmerbar rullning (scrollToItem, animateScrollToItem). LazyListState skapas via rememberLazyListState() och skickas till LazyColumn via parametern state.

kotlin
// LazyColumn med bevarad rullningsposition
@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)
            }
        }

        // Knappen "Upp" visas efter 10:e elementet
        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, "Till toppen")
            }
        }
    }
}

// Hämta information om synliga element
@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("Visar $firstItem–$lastItem av $totalItems")
}

Programmerbar rullning utförs genom suspend-funktioner i listState: scrollToItem(index, scrollOffset) — omedelbar förflyttning (utan animering), animateScrollToItem(index) — med animering. För rullning från en korutin, använd LaunchedEffect eller coroutineScope.launch. Viktigt: anropa inte suspend-funktioner från komposition (inuti @Composable) — använd lambdas (onClick, LaunchedEffect).

LazyColumn vs RecyclerView: jämförelse av tillvägagångssätt

LazyColumn och RecyclerView löser samma uppgift — effektiv visning av långa listor. Skillnaden är i arkitekturen: RecyclerView använder ViewHolder och Adapter (klassisk Android View), LazyColumn — deklarativa composable-funktioner utan XML och ViewHolder. Valet beror på projektets teknologistack: Compose vs View-based UI.

ParameterLazyColumn (Compose)RecyclerView (View)
LayoutComposable-funktioner (Kotlin DSL)XML-filer + ViewBinding
Adapteritems() / itemsIndexed() DSLRecyclerView.Adapter + ViewHolder
Sortering/filtreringSnapshot state + derivedStateOfDiffUtil + AsyncListDiffer
AnimeringAnimatedVisibility + Modifier.animateItemDefaultItemAnimator
Olika elementtyperitem {}/items() + when efter typgetItemViewType + flera ViewHolders
Rullning till positionlistState.animateScrollToItem()layoutManager.scrollToPosition()
PrefetchIn-process (SubcomposeLayout-buffert)RecycledViewPool + GapWorker
Nätverk/pagineringcollectAsLazyPagingItems() + Paging 3PagingDataAdapter + Paging 3

Googles rekommendation (Android Developers, 2026): för nya projekt på Jetpack Compose, använd LazyColumn. För View-based projekt, använd RecyclerView. LazyColumn kräver Compose, vilket lägger till cirka 3–5 MB i APK — ta hänsyn till detta vid support av äldre enheter. Om projektet redan använder View-systemet kan LazyColumn inte samexistera på samma skärm med XML-layout utan AndroidView — i sådana fall är det enklare att stanna kvar på RecyclerView.

LazyVerticalGrid och LazyHorizontalGrid: rutnät

LazyVerticalGrid — version av LazyColumn för rutnät med fast antal kolumner (columns). LazyHorizontalGrid — horisontellt rutnät med fast antal rader (rows). Båda komponenterna använder samma principer för lat lastning och samma items API som LazyColumn. Parametern columns bestämmer antalet kolumner via GridCells.Fixed(N) eller GridCells.Adaptive(minSize).

kotlin
// Rutnät med adaptiva kolumner
@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)
            )
        }
    }
}

// Horisontell lista (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 beräknar automatiskt antalet kolumner baserat på minimistorleken på cellen. Till exempel, GridCells.Adaptive(128.dp) på en skärm med bredden 360 dp placerar 2 kolumner, på en surfplatta 600 dp — 4 kolumner. GridCells.Fixed(2) visar alltid exakt 2 kolumner. Adaptive är att föredra för multi-skärm-anpassning — den anpassar sig automatiskt till skärmbredden utan media-frågor.

Prestanda för LazyColumn: key, contentType, prefetch

Prestanda för LazyColumn beror på tre faktorer: stabilitet hos nycklar (key), innehållstyp (contentType) och prefetch-buffert (beyondBounds). Som standard buffrar LazyColumn 1 skärm framåt och 1 bakåt. Google (Android Developers, 2026) rekommenderar att konfigurera dessa parametrar för listor med olika elementtyper eller komplex komposition.

ParameterBeskrivningRekommendation
keyStabil identifierare för elementAnge alltid key = { it.id }. Utan key använder Compose index och omordning av element förstör animeringen.
contentTypeInnehållstyp för separering av poolerAnge för listor med olika elementtyper (text + bild + annons). Compose återanvänder komposition endast inom samma contentType.
beyondBoundsAntal skärmar för prefetchÖka till 2–3 för listor med tunga bilder. Standard 1 skärm.
Modifier.animateItemAnimering av positionsändringAnvänd för animerad sortering och filtrering (Compose 1.7+).
kotlin
// Optimerad LazyColumn med contentType och prefetch
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
    val listState = rememberLazyListState()

    LazyColumn(
        state = listState,
        modifier = Modifier.fillMaxSize(),
        beyondBoundsPageCount = 2  // prefetch 2 skärmar
    ) {
        items(
            items = items,
            key = { it.id },
            contentType = { it.type }  // separering efter typ
        ) { item ->
            when (item) {
                is FeedItem.Post -> PostView(item)
                is FeedItem.Ad -> AdView(item)
                is FeedItem.Suggested -> SuggestedView(item)
            }
        }
    }
}

// Prestandamätning via listState
@Composable
fun ScrollPerformance(listState: LazyListState) {
    val scrollInfo = listState.layoutInfo
    val visibleCount = scrollInfo.visibleItemsInfo.size
    val total = scrollInfo.totalItemsCount

    // derivedStateOf — komponeras inte om om värdet inte har ändrats
    val scrollProgress by remember {
        derivedStateOf {
            if (total > 0) visibleCount.toFloat() / total else 0f
        }
    }

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

Vanliga misstag: (1) avsaknad av key — element komponeras om vid varje ändring av listan; (2) användning av mutableStateListOf utan snapshot-strömmar — ändringar kan inte spåras; (3) anrop av suspend-funktioner inuti itemContent — avbryter komposition; (4) utan contentType för blandade typer — Compose återanvänder annonskomposition för textinlägg, vilket orsakar visuella artefakter. Hos IT Sectr lägger vi till contentType i alla listor med tre eller fler innehållstyper.

Vanliga frågor

LazyColumn rullar långsamt — vad göra?

Kontrollera tre parametrar: (1) key — utan stabil nyckel kan Compose inte återanvända komposition, (2) contentType — för listor med olika elementtyper, (3) beyondBoundsPageCount — öka till 2 för bilder. Flytta tunga beräkningar inuti itemContent till remember med nyckel baserad på data. Använd Modifier.drawWithContent istället för Image för enkel grafik.

Hur lägger jag till avskiljare mellan element i LazyColumn?

Använd LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) för mellanrum mellan element. För visuell avskiljare med linje, lägg till item { Divider() } mellan items. Automatisk avskiljare: items(items, key = { it.id }) { item -> ... }, och mellan varje element sätt in item { HorizontalDivider() }. För detta använd LazyListScope.items(list, key) { /* element */ } och separat LazyListScope.item { Divider() }.

LazyColumn inuti Column fungerar inte — varför?

LazyColumn har oändlig höjd (fillMaxSize) som standard. Inuti Column utan fast höjd kan LazyColumn inte bestämma storlek. Lösning: (1) ge LazyColumn fast höjd: Modifier.height(400.dp), (2) använd Modifier.weight(1f) inuti Column, (3) placera inte LazyColumn i en vertikalt rullningsbar behållare — använd LazyColumn som rot-element med item {} för header och footer.

Hur uppdaterar jag LazyColumn vid dataändring?

LazyColumn reagerar automatiskt på ändringar i State. Om data lagras i mutableStateListOf eller mutableStateOf komponerar Compose om endast ändrade element. För listor från ViewModel, använd collectAsState() med Flow. Vid uppdatering av listan med key animerar Compose automatiskt ändringar (tillägg, borttagning, flyttning) genom Modifier.animateItemPlacement().

LazyColumn vs LazyVerticalGrid — vad välja?

LazyColumn — för listor med en kolumn (feed, chatt, kommentarer). LazyVerticalGrid — för rutnät (galleri, katalog, ikoner). Om element ska vara i en kolumn på telefonen och två kolumner på surfplattan — använd LazyVerticalGrid med GridCells.Adaptive. Om du behöver ett blandat rutnät (olika antal kolumner i en sektion) — använd LazyColumn och inuti items sätt in horisontell LazyRow eller nästlade LazyVerticalGrid.

Sammanfattning

  • LazyColumn — Jetpack Compose-komponent för lata vertikala listor med deklarativt API.
  • items() API accepterar lista och DSL-block för varje element; key är obligatorisk för prestanda.
  • LazyListState hanterar rullningsposition, tillhandahåller suspend-funktioner för programmerbar rullning.
  • contentType separerar kompositionspooler för listor med olika elementtyper.
  • LazyVerticalGrid / LazyRow — varianter för rutnät och horisontella listor med samma items API.
  • LazyColumn ger 60 FPS för 10000+ element tack vare SubcomposeLayout och prefetch-buffert.
  • För nya Compose-projekt använd LazyColumn; för View-based projekt — RecyclerView.

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.

Diskutera projektet

Läs också