LazyColumn — ano ito, mahusay na listahan sa Compose

May-akda: IT Sectr Nai-publish: 2026-02-24 Oras ng pagbabasa: 10 min

LazyColumn — bahagi ng Jetpack Compose para sa pagpapakita ng mga nai-scroll na listahan na may tamad na pag-load. Hindi tulad ng RecyclerView, ang LazyColumn ay hindi lumilikha ng ViewHolder at hindi gumagamit ng XML — lahat ng elemento ay inilalarawan nang deklaratibo sa pamamagitan ng mga composable function. Ipinapakita namin kung ano ang LazyColumn, kung paano gumagana ang LazyListState at items API, at kung paano makamit ang pagganap sa antas ng 60 FPS. Sa IT Sectr ginagamit namin ang LazyColumn sa lahat ng bagong Compose na proyekto. Para sa paghahambing sa klasikong diskarte, basahin ang artikulo tungkol sa RecyclerView.

Mga Pangunahing Punto

  • LazyColumn — bahagi ng Jetpack Compose para sa mga vertical na listahan na may tamad na pag-load, bahagi ng Material Design 3.
  • LazyListState — estado ng listahan: posisyon ng pag-scroll, mga nakikitang elemento, programatikong pag-scroll.
  • items API — mga function na items(), itemsIndexed(), items() na may mga susi para sa pagbubuklod ng datos.
  • key — parameter para sa matatag na pagkilala ng mga elemento, na nagpapahintulot sa Compose na muling gamitin ang komposisyon.
  • LazyVerticalGrid — bersyon ng LazyColumn para sa mga grid na may nakapirming bilang ng mga column.

Ano ang LazyColumn sa Jetpack Compose?

LazyColumn — ay isang composable function mula sa Foundation Compose library na nagpapakita ng vertical na listahan ng mga elemento na may tamad na (lazy) pag-load: tanging ang mga elementong nakikita sa screen o malapit dito (prefetch zone) ang nilikha at binubuo. Lumitaw ang LazyColumn sa Compose 1.0 (2021) kasama ang LazyRow (horizontal na listahan) at LazyVerticalGrid (grid).

Tamad na pag-load ay gumagana sa pamamagitan ng mekanismong SubcomposeLayout: sinusukat ng LazyColumn ang available na espasyo, hinihiling ang nakikitang saklaw mula sa LayoutInfo at binubuo lamang ang mga elemento sa saklaw na iyon. Ang mga elementong umalis na sa screen ay umaalis sa komposisyon (maliban sa prefetch buffer). Ayon sa datos ng Google (Android Performance, 2026), ang LazyColumn ay nagbibigay ng 60 FPS kapag nag-i-scroll ng listahan ng 10000+ elemento sa mga mid-range na device.

Pagkakaiba sa Column + ScrollState

Column na may vertical na pag-scroll — ay ang karaniwang paglalagay ng lahat ng elemento sa ilalim ng bawat isa na may aplikasyon ng verticalScroll modifier. Ang Column ay hindi tamad: lahat ng child element ay agad na binubuo, kahit na hindi sila nakikita. Para sa listahan ng 200+ elemento, ang Column ay nagdudulot ng malaking lag sa pagsisimula. Ang LazyColumn ay bumubuo lamang ng mga nakikitang elemento + buffer (default 1 screen pasulong/paurong), ginagawa itong kailangang-kailangan para sa mahabang listahan.

items API: pagbubuklod ng datos sa listahan

items API — set ng mga extension function para sa LazyColumn na tumatanggap ng listahan ng datos at DSL descriptor para sa bawat elemento. Pangunahing function: items(count, key, itemContent) para sa nakapirming bilang, items(list, key, itemContent) para sa listahan, itemsIndexed(list, key, itemContent) na may index. Ang parameter na key — sapilitang kondisyon para sa pagganap — ay nagbibigay sa Compose ng matatag na identifier ng elemento.

kotlin
// Pangunahing LazyColumn na may itemContent
@Composable
fun ArticleList(articles: List<Article>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Header — hindi tamad, palagi sa simula
        item {
            Text("Mga pinakabagong artikulo", style = MaterialTheme.typography.headlineMedium)
        }

        // Listahan ng mga artikulo na may susi sa id
        items(articles, key = { it.id }) { article ->
            ArticleCard(
                title = article.title,
                summary = article.summary,
                onClick = { onArticleClick(article.id) }
            )
        }

        // Footer na may tagapagpahiwatig ng pag-load
        item {
            CircularProgressIndicator(
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            )
        }
    }
}

// Pasadyang elemento ng listahan
@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(): gamitin ang item {} para sa mga solong elemento na laging nasa listahan (header, footer, separator). Gamitin ang items() para sa dynamic na listahan ng datos. Ang items() ay tumatanggap ng Iterable o Int at isang DSL block na tinatawag para sa bawat elemento. Pagsamahin ang item at items sa iisang LazyColumn: ang pagkakasunod-sunod ng tawag ay tumutukoy sa pagkakasunod-sunod ng pagpapakita.

LazyListState: pamamahala ng posisyon ng pag-scroll

LazyListState — bagay na nag-iimbak ng estado ng listahan: kasalukuyang posisyon ng pag-scroll (firstVisibleItemIndex, firstVisibleItemScrollOffset), mga nakikitang elemento (layoutInfo) at mga pamamaraan ng programatikong pag-scroll (scrollToItem, animateScrollToItem). Ang LazyListState ay nilikha sa pamamagitan ng rememberLazyListState() at ipinapasa sa LazyColumn sa pamamagitan ng parameter na state.

kotlin
// LazyColumn na may pagpapanatili ng posisyon ng pag-scroll
@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)
            }
        }

        // Lumilitaw ang button na "Pataas" pagkatapos ng ika-10 elemento
        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, "Tuktok")
            }
        }
    }
}

// Pagkuha ng impormasyon tungkol sa mga nakikitang elemento
@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("Ipinapakita ang $firstItem–$lastItem mula sa $totalItems")
}

Programatikong pag-scroll ay isinasagawa sa pamamagitan ng suspend function ng listState: scrollToItem(index, scrollOffset) — agarang paglipat (walang animasyon), animateScrollToItem(index) — may animasyon. Para sa pag-scroll mula sa coroutine gamitin ang LaunchedEffect o coroutineScope.launch. Mahalaga: huwag tumawag ng suspend function mula sa komposisyon (sa loob ng @Composable) — gumamit ng lambda (onClick, LaunchedEffect).

LazyColumn vs RecyclerView: paghahambing ng mga diskarte

LazyColumn at RecyclerView ay lumulutas ng parehong gawain — mahusay na pagpapakita ng mahabang listahan. Ang pagkakaiba ay nasa arkitektura: ang RecyclerView ay gumagamit ng ViewHolder at Adapter (klasikong Android View), ang LazyColumn — deklaratibong composable function na walang XML at ViewHolder. Ang pagpili ay depende sa technology stack ng proyekto: Compose vs View-based UI.

ParameterLazyColumn (Compose)RecyclerView (View)
LayoutMga Composable function (Kotlin DSL)Mga XML file + ViewBinding
Adapteritems() / itemsIndexed() DSLRecyclerView.Adapter + ViewHolder
Pag-uuri/pagsalaSnapshot state + derivedStateOfDiffUtil + AsyncListDiffer
AnimasyonAnimatedVisibility + Modifier.animateItemDefaultItemAnimator
Iba't ibang uri ng elementoitem {}/items() + when ayon sa urigetItemViewType + maramihang ViewHolder
Pag-scroll sa posisyonlistState.animateScrollToItem()layoutManager.scrollToPosition()
PrefetchIn-process (SubcomposeLayout buffer)RecycledViewPool + GapWorker
Network/paginasyoncollectAsLazyPagingItems() + Paging 3PagingDataAdapter + Paging 3

Rekomendasyon ng Google (Android Developers, 2026): para sa mga bagong proyekto sa Jetpack Compose gamitin ang LazyColumn. Para sa View-based na proyekto gamitin ang RecyclerView. Ang LazyColumn ay nangangailangan ng Compose, na nagdaragdag ng humigit-kumulang 3–5 MB sa APK — isaalang-alang ito kapag sumusuporta sa mga lumang device. Kung ang proyekto ay gumagamit na ng View system, ang LazyColumn ay hindi maaaring magkasama sa parehong screen na may XML layout nang walang AndroidView — sa mga ganitong kaso mas madaling manatili sa RecyclerView.

LazyVerticalGrid at LazyHorizontalGrid: mga grid

LazyVerticalGrid — bersyon ng LazyColumn para sa mga grid na may nakapirming bilang ng mga column (columns). LazyHorizontalGrid — horizontal na grid na may nakapirming bilang ng mga row (rows). Parehong ginagamit ng dalawang bahagi ang parehong prinsipyo ng tamad na pag-load at parehong items API tulad ng LazyColumn. Tinutukoy ng parameter na columns ang bilang ng mga column sa pamamagitan ng GridCells.Fixed(N) o GridCells.Adaptive(minSize).

kotlin
// Grid na may adaptibong mga column
@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)
            )
        }
    }
}

// Horizontal na listahan (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 ay awtomatikong kinakalkula ang bilang ng mga column batay sa minimum na laki ng cell. Halimbawa, ang GridCells.Adaptive(128.dp) sa isang screen na 360 dp ang lapad ay naglalagay ng 2 column, sa isang tablet na 600 dp — 4 na column. GridCells.Fixed(2) ay palaging nagpapakita ng eksaktong 2 column. Ang Adaptive ay mas mainam para sa multi-screen adaptation — awtomatiko itong umaangkop sa lapad ng screen nang walang media query.

Pagganap ng LazyColumn: key, contentType, prefetch

Pagganap ng LazyColumn ay nakadepende sa tatlong factor: katatagan ng mga susi (key), uri ng nilalaman (contentType) at prefetch buffer (beyondBounds). Bilang default, ang LazyColumn ay nagba-buffer ng 1 screen pasulong at 1 paurong. Ang Google (Android Developers, 2026) ay nagrerekomenda na i-configure ang mga parameter na ito para sa mga listahan na may iba't ibang uri ng elemento o kumplikadong komposisyon.

ParameterPaglalarawanRekomendasyon
keyMatatag na identifier ng elementoPalaging tukuyin ang key = { it.id }. Kung walang key, ginagamit ng Compose ang index, at ang pag-reorder ng mga elemento ay sumisira ng animasyon.
contentTypeUri ng nilalaman para sa paghihiwalay ng mga poolTukuyin para sa mga listahan na may iba't ibang uri ng elemento (teksto + larawan + ad). Ang Compose ay muling gagamit ng komposisyon lamang sa loob ng parehong contentType.
beyondBoundsBilang ng mga screen para sa prefetchTaasan sa 2–3 para sa mga listahan na may mabibigat na larawan. Default 1 screen.
Modifier.animateItemAnimasyon ng pagbabago ng posisyonGamitin para sa animated na pag-uuri at pagsala (Compose 1.7+).
kotlin
// Na-optimize na LazyColumn na may contentType at prefetch
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
    val listState = rememberLazyListState()

    LazyColumn(
        state = listState,
        modifier = Modifier.fillMaxSize(),
        beyondBoundsPageCount = 2  // prefetch 2 screen
    ) {
        items(
            items = items,
            key = { it.id },
            contentType = { it.type }  // paghihiwalay ayon sa uri
        ) { item ->
            when (item) {
                is FeedItem.Post -> PostView(item)
                is FeedItem.Ad -> AdView(item)
                is FeedItem.Suggested -> SuggestedView(item)
            }
        }
    }
}

// Pagsukat ng pagganap sa pamamagitan ng listState
@Composable
fun ScrollPerformance(listState: LazyListState) {
    val scrollInfo = listState.layoutInfo
    val visibleCount = scrollInfo.visibleItemsInfo.size
    val total = scrollInfo.totalItemsCount

    // derivedStateOf — hindi muling binubuo kung ang halaga ay hindi nagbago
    val scrollProgress by remember {
        derivedStateOf {
            if (total > 0) visibleCount.toFloat() / total else 0f
        }
    }

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

Mga karaniwang pagkakamali: (1) kawalan ng key — ang mga elemento ay muling binubuo sa bawat pagbabago ng listahan; (2) paggamit ng mutableStateListOf nang walang snapshot stream — ang mga pagbabago ay maaaring hindi masubaybayan; (3) pagtawag ng suspend function sa loob ng itemContent — pumutol sa komposisyon; (4) walang contentType para sa mixed type — ginagamit muli ng Compose ang komposisyon ng ad para sa text post, na nagdudulot ng visual artifacts. Sa IT Sectr nagdaragdag kami ng contentType sa lahat ng listahan na may tatlo o higit pang uri ng nilalaman.

Mga Madalas Itanong

Ang LazyColumn ay mabagal mag-scroll — ano ang gagawin?

Suriin ang tatlong parameter: (1) key — kung walang matatag na susi, hindi magagamit muli ng Compose ang komposisyon, (2) contentType — para sa mga listahan na may iba't ibang uri ng elemento, (3) beyondBoundsPageCount — taasan sa 2 para sa mga larawan. Ilipat ang mabibigat na kalkulasyon sa loob ng itemContent sa remember na may susi batay sa datos. Gamitin ang Modifier.drawWithContent sa halip na Image para sa simpleng graphics.

Paano magdagdag ng separator sa pagitan ng mga elemento ng LazyColumn?

Gamitin ang LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) para sa mga puwang sa pagitan ng mga elemento. Para sa visual na separator na may linya, magdagdag ng item { Divider() } sa pagitan ng items. Awtomatikong separator: items(items, key = { it.id }) { item -> ... }, at sa pagitan ng bawat elemento ay magsingit ng item { HorizontalDivider() }. Para dito gamitin ang LazyListScope.items(list, key) { /* elemento */ } at hiwalay na LazyListScope.item { Divider() }.

Ang LazyColumn sa loob ng Column ay hindi gumagana — bakit?

LazyColumn ay may walang hanggan na taas (fillMaxSize) bilang default. Sa loob ng Column na walang nakapirming taas, hindi matutukoy ng LazyColumn ang laki. Solusyon: (1) bigyan ang LazyColumn ng nakapirming taas: Modifier.height(400.dp), (2) gamitin ang Modifier.weight(1f) sa loob ng Column, (3) huwag ilagay ang LazyColumn sa isang vertical na nai-scroll na lalagyan — gamitin ang LazyColumn bilang root element na may item {} para sa header at footer.

Paano i-update ang LazyColumn kapag nagbago ang datos?

LazyColumn ay awtomatikong tumutugon sa mga pagbabago sa State. Kung ang datos ay naka-imbak sa mutableStateListOf o mutableStateOf, ang Compose ay muling bubuo lamang ng mga elementong nagbago. Para sa mga listahan mula sa ViewModel gamitin ang collectAsState() na may Flow. Kapag nag-a-update ng listahan na may key, awtomatikong ina-animate ng Compose ang mga pagbabago (pagdagdag, pagtanggal, paglipat) sa pamamagitan ng Modifier.animateItemPlacement().

LazyColumn vs LazyVerticalGrid — alin ang pipiliin?

LazyColumn — para sa mga listahan na may isang column (feed, chat, komento). LazyVerticalGrid — para sa mga grid (galerya, katalogo, icon). Kung ang mga elemento ay dapat nasa isang column sa telepono at dalawang column sa tablet — gamitin ang LazyVerticalGrid na may GridCells.Adaptive. Kung kailangan mo ng mixed grid (iba't ibang bilang ng column sa isang seksyon) — gamitin ang LazyColumn at sa loob ng items ay magsingit ng horizontal na LazyRow o nested na LazyVerticalGrid.

Buod

  • LazyColumn — bahagi ng Jetpack Compose para sa mga tamad na vertical na listahan na may deklaratibong API.
  • items() API ay tumatanggap ng listahan at DSL block para sa bawat elemento; key ay sapilitan para sa pagganap.
  • LazyListState ay namamahala ng posisyon ng pag-scroll, nagbibigay ng suspend function para sa programatikong pag-scroll.
  • contentType ay naghihiwalay ng mga pool ng komposisyon para sa mga listahan na may iba't ibang uri ng elemento.
  • LazyVerticalGrid / LazyRow — mga variant para sa mga grid at horizontal na listahan na may parehong items API.
  • Ang LazyColumn ay nagbibigay ng 60 FPS para sa 10000+ elemento salamat sa SubcomposeLayout at prefetch buffer.
  • Para sa mga bagong Compose na proyekto gamitin ang LazyColumn; para sa View-based na proyekto — RecyclerView.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din