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
items(), itemsIndexed(), items() na may mga susi para sa pagbubuklod ng datos.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.
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 — 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.
// 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 — 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.
// 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 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.
| Parameter | LazyColumn (Compose) | RecyclerView (View) |
|---|---|---|
| Layout | Mga Composable function (Kotlin DSL) | Mga XML file + ViewBinding |
| Adapter | items() / itemsIndexed() DSL | RecyclerView.Adapter + ViewHolder |
| Pag-uuri/pagsala | Snapshot state + derivedStateOf | DiffUtil + AsyncListDiffer |
| Animasyon | AnimatedVisibility + Modifier.animateItem | DefaultItemAnimator |
| Iba't ibang uri ng elemento | item {}/items() + when ayon sa uri | getItemViewType + maramihang ViewHolder |
| Pag-scroll sa posisyon | listState.animateScrollToItem() | layoutManager.scrollToPosition() |
| Prefetch | In-process (SubcomposeLayout buffer) | RecycledViewPool + GapWorker |
| Network/paginasyon | collectAsLazyPagingItems() + Paging 3 | PagingDataAdapter + 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 — 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).
// 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 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.
| Parameter | Paglalarawan | Rekomendasyon |
|---|---|---|
| key | Matatag na identifier ng elemento | Palaging tukuyin ang key = { it.id }. Kung walang key, ginagamit ng Compose ang index, at ang pag-reorder ng mga elemento ay sumisira ng animasyon. |
| contentType | Uri ng nilalaman para sa paghihiwalay ng mga pool | Tukuyin 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. |
| beyondBounds | Bilang ng mga screen para sa prefetch | Taasan sa 2–3 para sa mga listahan na may mabibigat na larawan. Default 1 screen. |
| Modifier.animateItem | Animasyon ng pagbabago ng posisyon | Gamitin para sa animated na pag-uuri at pagsala (Compose 1.7+). |
// 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
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.
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() }.
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.
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 — 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
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.
Basahin din