LazyColumn — mi ez, hatékony listák Compose-ban

Szerző: IT Sectr Megjelenés: 2026-02-24 Olvasási idő: 10 perc

LazyColumn — Jetpack Compose komponens görgethető listák lusta betöltéssel történő megjelenítéséhez. A RecyclerView-val ellentétben a LazyColumn nem hoz létre ViewHolder-okat és nem használ XML-t — minden elem deklaratívan, composable függvényeken keresztül kerül leírásra. Megmutatjuk, mi az a LazyColumn, hogyan működik a LazyListState és items API, és hogyan érhető el a 60 FPS teljesítmény. Az IT Sectr-nél a LazyColumn-t használjuk minden új Compose projektben. A klasszikus megközelítéssel való összehasonlításhoz olvassa el a RecyclerView-ról szóló cikket.

Főbb pontok

  • LazyColumn — Jetpack Compose komponens függőleges listákhoz lusta betöltéssel, a Material Design 3 része.
  • LazyListState — lista állapota: görgetési pozíció, látható elemek, programozott görgetés.
  • items APIitems(), itemsIndexed(), items() függvények kulcsokkal az adatok kötéséhez.
  • key — paraméter az elemek stabil azonosításához, lehetővé téve a Compose számára a kompozíció újrafelhasználását.
  • LazyVerticalGrid — a LazyColumn verziója rácshoz fix oszlopszámmal.

Mi az a LazyColumn Jetpack Compose-ban?

LazyColumn — egy composable függvény a Foundation Compose könyvtárból, amely egy függőleges elemekből álló listát jelenít meg lusta (lazy) betöltéssel: csak azok az elemek jönnek létre és komponálódnak, amelyek láthatók a képernyőn vagy annak közelében vannak (prefetch zóna). A LazyColumn a Compose 1.0-ban (2021) jelent meg a LazyRow (vízszintes lista) és a LazyVerticalGrid (rács) mellett.

Lusta betöltés a SubcomposeLayout mechanizmuson keresztül működik: a LazyColumn megméri a rendelkezésre álló helyet, lekéri a látható tartományt a LayoutInfo-tól, és csak az ebben a tartományban lévő elemeket komponálja. A képernyőről eltűnt elemek elhagyják a kompozíciót (kivéve a prefetch puffert). A Google adatai szerint (Android Performance, 2026) a LazyColumn 60 FPS-t biztosít egy 10000+ elemből álló lista görgetésekor középkategóriás eszközökön.

Különbség a Column + ScrollState-től

Column függőleges görgetéssel — az összes elem egymás alá rendezése a verticalScroll módosító alkalmazásával. A Column nem lusta: az összes gyermekelem azonnal komponálódik, még akkor is, ha nem láthatók. Egy 200+ elemű lista esetén a Column jelentős késleltetést okoz induláskor. A LazyColumn csak a látható elemeket + puffert (alapértelmezés szerint 1 képernyő előre/hátra) komponálja, ami nélkülözhetetlenné teszi hosszú listák esetén.

items API: adatok kötése a listához

items API — kiterjesztő függvények halmaza a LazyColumn-hoz, amelyek egy adatlistát és egy DSL leírót fogadnak el minden elemhez. Fő függvények: items(count, key, itemContent) fix számhoz, items(list, key, itemContent) listához, itemsIndexed(list, key, itemContent) indexszel. A key paraméter — kötelező feltétel a teljesítményhez — stabil azonosítót ad a Compose-nak az elemhez.

kotlin
// Alap LazyColumn itemContent-tel
@Composable
fun ArticleList(articles: List<Article>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Header — nem lusta, mindig az elején
        item {
            Text("Legutóbbi cikkek", style = MaterialTheme.typography.headlineMedium)
        }

        // Cikkek listája id kulccsal
        items(articles, key = { it.id }) { article ->
            ArticleCard(
                title = article.title,
                summary = article.summary,
                onClick = { onArticleClick(article.id) }
            )
        }

        // Footer betöltési jelzővel
        item {
            CircularProgressIndicator(
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            )
        }
    }
}

// Egyéni listaelem
@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(): használja a item {}-t egyedi elemekhez, amelyek mindig jelen vannak a listában (header, footer, elválasztók). Használja a items()-t dinamikus adatlistához. Az items() Iterable vagy Int típust és egy DSL blokkot fogad el, amely minden elemhez meghívásra kerül. Kombinálja az item és items elemeket egy LazyColumn-ban: a hívások sorrendje határozza meg a megjelenítés sorrendjét.

LazyListState: görgetési pozíció kezelése

LazyListState — objektum, amely tárolja a lista állapotát: aktuális görgetési pozíció (firstVisibleItemIndex, firstVisibleItemScrollOffset), látható elemek (layoutInfo) és programozott görgetési módszerek (scrollToItem, animateScrollToItem). A LazyListState a rememberLazyListState() segítségével jön létre, és a state paraméteren keresztül kerül átadásra a LazyColumn-nak.

kotlin
// LazyColumn görgetési pozíció megtartásával
@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)
            }
        }

        // A "Fel" gomb a 10. elem után jelenik meg
        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, "Felülre")
            }
        }
    }
}

// Információk lekérése a látható elemekről
@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("$firstItem–$lastItem megjelenítve $totalItems-ból")
}

Programozott görgetés a listState suspend függvényein keresztül történik: scrollToItem(index, scrollOffset) — azonnali mozgatás (animáció nélkül), animateScrollToItem(index) — animációval. Korutinból történő görgetéshez használja a LaunchedEffect vagy coroutineScope.launch függvényeket. Fontos: ne hívjon suspend függvényeket kompozícióból (@Composable-en belül) — használjon lambdákat (onClick, LaunchedEffect).

LazyColumn vs RecyclerView: megközelítések összehasonlítása

LazyColumn és RecyclerView ugyanazt a feladatot oldja meg — hosszú listák hatékony megjelenítését. A különbség az architektúrában van: a RecyclerView ViewHolder-t és Adapter-t használ (klasszikus Android View), a LazyColumn — deklaratív composable függvényeket XML és ViewHolder nélkül. A választás a projekt technológiai stackjétől függ: Compose vs View-based UI.

ParaméterLazyColumn (Compose)RecyclerView (View)
ElrendezésComposable függvények (Kotlin DSL)XML fájlok + ViewBinding
Adapteritems() / itemsIndexed() DSLRecyclerView.Adapter + ViewHolder
Rendezés/szűrésSnapshot state + derivedStateOfDiffUtil + AsyncListDiffer
AnimációAnimatedVisibility + Modifier.animateItemDefaultItemAnimator
Különböző elem típusokitem {}/items() + when típus szerintgetItemViewType + több ViewHolder
Görgetés pozícióralistState.animateScrollToItem()layoutManager.scrollToPosition()
PrefetchIn-process (SubcomposeLayout puffer)RecycledViewPool + GapWorker
Hálózat/lapozáscollectAsLazyPagingItems() + Paging 3PagingDataAdapter + Paging 3

Google ajánlás (Android Developers, 2026): új Jetpack Compose projektekhez használja a LazyColumn-t. View-alapú projektekhez használja a RecyclerView-t. A LazyColumn Compose-t igényel, ami körülbelül 3–5 MB-ot ad az APK-hoz — ezt vegye figyelembe a régi eszközök támogatásánál. Ha a projekt már View rendszert használ, a LazyColumn nem létezhet együtt ugyanazon a képernyőn XML elrendezéssel AndroidView nélkül — ilyen esetekben egyszerűbb a RecyclerView-nál maradni.

LazyVerticalGrid és LazyHorizontalGrid: rácsok

LazyVerticalGrid — a LazyColumn verziója fix oszlopszámú (columns) rácsokhoz. LazyHorizontalGrid — vízszintes rács fix sorokkal (rows). Mindkét komponens ugyanazokat a lusta betöltési elveket és ugyanazt az items API-t használja, mint a LazyColumn. A columns paraméter GridCells.Fixed(N) vagy GridCells.Adaptive(minSize) segítségével határozza meg az oszlopok számát.

kotlin
// Rács adaptív oszlopokkal
@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)
            )
        }
    }
}

// Vízszintes 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 automatikusan kiszámítja az oszlopok számát a cella minimális mérete alapján. Például a GridCells.Adaptive(128.dp) egy 360 dp széles képernyőn 2 oszlopot, egy 600 dp-s táblagépen 4 oszlopot helyez el. A GridCells.Fixed(2) mindig pontosan 2 oszlopot mutat. Az Adaptive előnyösebb a többképernyős adaptációhoz — automatikusan alkalmazkodik a képernyő szélességéhez médialekérdezések nélkül.

LazyColumn teljesítmény: key, contentType, prefetch

Teljesítmény A LazyColumn három tényezőtől függ: a kulcsok stabilitásától (key), a tartalom típusától (contentType) és a prefetch puffer méretétől (beyondBounds). Alapértelmezés szerint a LazyColumn 1 képernyőt pufferel előre és 1-et hátra. A Google (Android Developers, 2026) javasolja ezen paraméterek konfigurálását különböző elem típusú vagy összetett kompozíciójú listákhoz.

ParaméterLeírásAjánlás
keyAz elem stabil azonosítójaMindig adja meg a key = { it.id } paramétert. Key nélkül a Compose indexet használ, és az elemek átrendezése elrontja az animációt.
contentTypeTartalom típusa a poolok szétválasztásáhozAdja meg különböző elem típusú listákhoz (szöveg + kép + hirdetés). A Compose csak ugyanazon contentType-on belül használja újra a kompozíciót.
beyondBoundsKépernyők száma a prefetch-hezNövelje 2–3-ra nehéz képeket tartalmazó listák esetén. Alapértelmezés: 1 képernyő.
Modifier.animateItemPozícióváltozás animációjaAnimált rendezéshez és szűréshez használja (Compose 1.7+).
kotlin
// Optimalizált LazyColumn contentType és prefetch segítségével
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
    val listState = rememberLazyListState()

    LazyColumn(
        state = listState,
        modifier = Modifier.fillMaxSize(),
        beyondBoundsPageCount = 2  // prefetch 2 képernyő
    ) {
        items(
            items = items,
            key = { it.id },
            contentType = { it.type }  // szétválasztás típusok szerint
        ) { item ->
            when (item) {
                is FeedItem.Post -> PostView(item)
                is FeedItem.Ad -> AdView(item)
                is FeedItem.Suggested -> SuggestedView(item)
            }
        }
    }
}

// Teljesítménymérés listState-en keresztül
@Composable
fun ScrollPerformance(listState: LazyListState) {
    val scrollInfo = listState.layoutInfo
    val visibleCount = scrollInfo.visibleItemsInfo.size
    val total = scrollInfo.totalItemsCount

    // derivedStateOf — nem komponálódik újra, ha az érték nem változott
    val scrollProgress by remember {
        derivedStateOf {
            if (total > 0) visibleCount.toFloat() / total else 0f
        }
    }

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

Gyakori hibák: (1) key hiánya — az elemek újrakomponálódnak a lista minden változásakor; (2) mutableStateListOf használata snapshot folyamok nélkül — a változások nem feltétlenül követhetők nyomon; (3) suspend függvények hívása itemContent-on belül — megszakítja a kompozíciót; (4) contentType hiánya vegyes típusokhoz — a Compose újrahasználja a hirdetés kompozícióját egy szöveges bejegyzéshez, vizuális hibákat okozva. Az IT Sectr-nél contentType-t adunk minden olyan listához, amely három vagy több tartalomtípust tartalmaz.

Gyakran Ismételt Kérdések

A LazyColumn lassan görget — mit tegyek?

Ellenőrizzen három paramétert: (1) key — stabil kulcs nélkül a Compose nem tudja újrahasználni a kompozíciót, (2) contentType — különböző elem típusú listákhoz, (3) beyondBoundsPageCount — növelje 2-re képek esetén. A nehéz számításokat itemContent-on belül helyezze át remember-be adatkulccsal. Egyszerű grafikához Image helyett használja a Modifier.drawWithContent függvényt.

Hogyan adhatok elválasztót a LazyColumn elemei közé?

Használja a LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) paramétert az elemek közötti távközökhöz. Vizuális elválasztóhoz vonallal adjon hozzá item { Divider() } elemeket az items között. Automatikus elválasztó: items(items, key = { it.id }) { item -> ... }, és minden elem közé illesszen be item { HorizontalDivider() } elemet. Ehhez használja a LazyListScope.items(list, key) { /* elem */ } és külön a LazyListScope.item { Divider() } függvényeket.

A LazyColumn a Column-on belül nem működik — miért?

LazyColumn alapértelmezés szerint végtelen magasságú (fillMaxSize). Fix magasság nélküli Column-on belül a LazyColumn nem tudja meghatározni a méretet. Megoldás: (1) adjon a LazyColumn-nak fix magasságot: Modifier.height(400.dp), (2) használja a Modifier.weight(1f) paramétert Column-on belül, (3) ne helyezze a LazyColumn-t függőlegesen görgethető konténerbe — használja a LazyColumn-t gyökérelemként item {} segítségével header és footer számára.

Hogyan frissíthetem a LazyColumn-t adatváltozáskor?

LazyColumn automatikusan reagál a State változásaira. Ha az adatok mutableStateListOf vagy mutableStateOf típusban vannak tárolva, a Compose csak a megváltozott elemeket komponálja újra. ViewModel-ből származó listákhoz használja a collectAsState() függvényt Flow-val. A lista key-vel történő frissítésekor a Compose automatikusan animálja a változásokat (hozzáadás, eltávolítás, áthelyezés) a Modifier.animateItemPlacement() segítségével.

LazyColumn vs LazyVerticalGrid — mit válasszak?

LazyColumn — egy oszlopos listákhoz (feed, chat, hozzászólások). LazyVerticalGrid — rácsokhoz (galéria, katalógus, ikonok). Ha az elemeknek a telefonon egy oszlopban, a táblagépen pedig két oszlopban kell lenniük — használja a LazyVerticalGrid-et GridCells.Adaptive beállítással. Ha vegyes rácsra van szükség (különböző oszlopszám egy szekcióban) — használja a LazyColumn-t, és az items-en belül illesszen be vízszintes LazyRow vagy egymásba ágyazott LazyVerticalGrid elemeket.

Összegzés

  • LazyColumn — Jetpack Compose komponens lusta függőleges listákhoz deklaratív API-val.
  • items() API listát és DSL blokkot fogad el minden elemhez; a key kötelező a teljesítményhez.
  • LazyListState kezeli a görgetési pozíciót, suspend függvényeket biztosít a programozott görgetéshez.
  • contentType szétválasztja a kompozíciós poolokat különböző elem típusú listákhoz.
  • LazyVerticalGrid / LazyRow — változatok rácsokhoz és vízszintes listákhoz ugyanazzal az items API-val.
  • A LazyColumn 60 FPS-t biztosít 10000+ elemhez a SubcomposeLayout és a prefetch puffer segítségével.
  • Új Compose projektekhez használja a LazyColumn-t; View-alapú projektekhez — RecyclerView-t.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is