LazyColumn — Jetpack Compose-ning kechiktirilgan yuklash bilan aylanma ro'yxatlarni ko'rsatish komponenti. RecyclerView-dan farqli o'laroq, LazyColumn ViewHolder yaratmaydi va XML ishlatmaydi — barcha elementlar composable-funksiyalar orqali deklarativ tarzda tavsiflanadi. LazyColumn nima ekanligini, LazyListState va items API qanday ishlashini va 60 FPS darajasida unumdorlikka qanday erishishni ko'rsatamiz. IT Sectr-da biz LazyColumn-dan barcha yangi Compose loyihalarida foydalanamiz. Klassik yondashuv bilan solishtirish uchun RecyclerView haqidagi maqolani o'qing.
Asosiy nuqtalar
items(), itemsIndexed(), items() funksiyalari.LazyColumn — Foundation Compose kutubxonasidan olingan, kechiktirilgan (lazy) yuklash bilan vertikal elementlar ro'yxatini ko'rsatadigan composable-funksiya: faqat ekranda ko'rinadigan yoki yaqin atrofda joylashgan (prefetch zonasi) elementlar yaratiladi va kompozitsiya qilinadi. LazyColumn Compose 1.0 (2021) bilan birga LazyRow (gorizontal ro'yxat) va LazyVerticalGrid (panjara) bilan birga paydo bo'ldi.
Kechiktirilgan yuklash SubcomposeLayout mexanizmi orqali ishlaydi: LazyColumn mavjud joyni o'lchaydi, LayoutInfo-dan ko'rinadigan diapazonni so'raydi va faqat shu diapazondagi elementlarni kompozitsiya qiladi. Ekrandan chiqqan elementlar kompozitsiyani tark etadi (prefetch buferi bundan mustasno). Google ma'lumotlariga ko'ra (Android Performance, 2026), LazyColumn o'rta segment qurilmalarida 10000+ elementli ro'yxatni aylantirishda 60 FPS ni ta'minlaydi.
Column vertikal aylantirish bilan — verticalScroll modifierini qo'llash bilan barcha elementlarni bir-birining ostiga oddiy joylashtirish. Column dangasa emas: barcha bola elementlar ko'rinmasa ham darhol kompozitsiya qilinadi. 200+ elementli ro'yxat uchun Column boshlanishda sezilarli kechikishga sabab bo'ladi. LazyColumn faqat ko'rinadiganlarni + buferni (standart 1 ekran oldinga/ortga) kompozitsiya qiladi, bu uni uzoq ro'yxatlar uchun ajralmas qiladi.
items API — LazyColumn uchun kengaytma funksiyalar to'plami, ma'lumotlar ro'yxatini va har bir element uchun DSL tavsifini qabul qiladi. Asosiy funksiyalar: sobit son uchun items(count, key, itemContent), ro'yxat uchun items(list, key, itemContent), indeks bilan itemsIndexed(list, key, itemContent). key parametri — unumdorlik uchun majburiy shart — Compose-ga elementning barqaror identifikatorini beradi.
// itemContent bilan asosiy LazyColumn
@Composable
fun ArticleList(articles: List<Article>) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// Header — dangasa emas, har doim boshida
item {
Text("Oxirgi maqolalar", style = MaterialTheme.typography.headlineMedium)
}
// Maqolalar ro'yxati id bilan kalit
items(articles, key = { it.id }) { article ->
ArticleCard(
title = article.title,
summary = article.summary,
onClick = { onArticleClick(article.id) }
)
}
// Yuklash ko'rsatkichi bilan Footer
item {
CircularProgressIndicator(
modifier = Modifier.fillMaxWidth().padding(16.dp)
)
}
}
}
// Maxsus ro'yxat elementi
@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(): ro'yxatda doimo mavjud bo'lgan yagona elementlar (header, footer, ajratgichlar) uchun item {} ishlating. Dinamik ma'lumotlar ro'yxati uchun items() ishlating. items() Iterable yoki Int va har bir element uchun chaqiriladigan DSL blokini qabul qiladi. Bitta LazyColumn-da item va items-ni birlashtiring: chaqiruv tartibi ko'rsatish tartibini belgilaydi.
LazyListState — ro'yxat holatini saqlaydigan obyekt: joriy aylantirish pozitsiyasi (firstVisibleItemIndex, firstVisibleItemScrollOffset), ko'rinadigan elementlar (layoutInfo) va dasturiy aylantirish usullari (scrollToItem, animateScrollToItem). LazyListState rememberLazyListState() orqali yaratiladi va state parametri orqali LazyColumn-ga uzatiladi.
// Aylantirish pozitsiyasini saqlaydigan LazyColumn
@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)
}
}
// "Yuqoriga" tugmasi 10-elementdan keyin paydo bo'ladi
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, "Tepaga")
}
}
}
}
// Ko'rinadigan elementlar haqida ma'lumot olish
@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 jami $totalItems dan ko'rsatilgan")
}
Dasturiy aylantirish listState-ning suspend-funksiyalari orqali bajariladi: scrollToItem(index, scrollOffset) — oniy harakat (animatsiyasiz), animateScrollToItem(index) — animatsiya bilan. Korutindan aylantirish uchun LaunchedEffect yoki coroutineScope.launch ishlating. Muhim: kompozitsiyadan (@Composable ichida) suspend-funksiyalarni chaqirmang — lambdalardan (onClick, LaunchedEffect) foydalaning.
LazyColumn va RecyclerView bir xil vazifani hal qiladi — uzoq ro'yxatlarning samarali ko'rsatilishi. Farq arxitekturada: RecyclerView ViewHolder va Adapter (klassik Android View) ishlatadi, LazyColumn — XML va ViewHoldersiz deklarativ composable-funksiyalar. Tanlov loyihaning texnologiya stekiga bog'liq: Compose vs View-based UI.
| Parametr | LazyColumn (Compose) | RecyclerView (View) |
|---|---|---|
| Layout | Composable-funksiyalar (Kotlin DSL) | XML fayllari + ViewBinding |
| Adapter | items() / itemsIndexed() DSL | RecyclerView.Adapter + ViewHolder |
| Saralash/filtratsiya | Snapshot state + derivedStateOf | DiffUtil + AsyncListDiffer |
| Animatsiya | AnimatedVisibility + Modifier.animateItem | DefaultItemAnimator |
| Turli element turlari | item {}/items() + turga ko'ra when | getItemViewType + ko'p ViewHolder-lar |
| Pozitsiyaga aylantirish | listState.animateScrollToItem() | layoutManager.scrollToPosition() |
| Prefetch | In-process (SubcomposeLayout buferi) | RecycledViewPool + GapWorker |
| Tarmoq/paginatsiya | collectAsLazyPagingItems() + Paging 3 | PagingDataAdapter + Paging 3 |
Google tavsiyasi (Android Developers, 2026): Jetpack Compose-dagi yangi loyihalar uchun LazyColumn ishlating. View-based loyihalar uchun RecyclerView ishlating. LazyColumn Compose talab qiladi, bu APK-ga taxminan 3–5 MB qo'shadi — eski qurilmalarni qo'llab-quvvatlashda buni hisobga oling. Agar loyiha allaqachon View tizimidan foydalanayotgan bo'lsa, LazyColumn AndroidViewsiz XML layout bilan bir ekranda mavjud bo'la olmaydi — bunday hollarda RecyclerView-da qolish osonroq.
LazyVerticalGrid — sobit sonli ustunlarga (columns) ega panjaralar uchun LazyColumn versiyasi. LazyHorizontalGrid — sobit sonli qatorlarga (rows) ega gorizontal panjara. Ikkala komponent LazyColumn bilan bir xil kechiktirilgan yuklash tamoyillaridan va bir xil items API-dan foydalanadi. columns parametri GridCells.Fixed(N) yoki GridCells.Adaptive(minSize) orqali ustunlar sonini belgilaydi.
// Adaptiv ustunlar bilan panjara
@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)
)
}
}
}
// Gorizontal ro'yxat (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 hujayraning minimal o'lchamiga asoslanib ustunlar sonini avtomatik hisoblaydi. Masalan, GridCells.Adaptive(128.dp) 360 dp kenglikdagi ekranda 2 ustunni, 600 dp planshetda 4 ustunni joylashtiradi. GridCells.Fixed(2) har doim aniq 2 ustunni ko'rsatadi. Adaptive ko'p ekranli moslashuv uchun afzal — media so'rovlarsiz ekran kengligiga avtomatik moslashadi.
Unumdorlik LazyColumn uch omilga bog'liq: kalitlarning barqarorligi (key), kontent turi (contentType) va prefetch buferi (beyondBounds). Standartda LazyColumn 1 ekran oldinga va 1 orqaga buferlaydi. Google (Android Developers, 2026) ushbu parametrlarni turli element turlari yoki murakkab kompozitsiyaga ega ro'yxatlar uchun sozlashni tavsiya qiladi.
| Parametr | Tavsif | Tavsiya |
|---|---|---|
| key | Elementning barqaror identifikatori | Har doim key = { it.id } ni belgilang. Keysiz Compose indeksdan foydalanadi va elementlarni qayta tartiblash animatsiyani buzadi. |
| contentType | Hovuzlarni ajratish uchun kontent turi | Turli element turlari (matn + rasm + reklama) bo'lgan ro'yxatlar uchun belgilang. Compose faqat bitta contentType ichida kompozitsiyani qayta ishlatadi. |
| beyondBounds | Prefetch uchun ekranlar soni | Og'ir rasmlari bo'lgan ro'yxatlar uchun 2–3 gacha oshiring. Standart 1 ekran. |
| Modifier.animateItem | Pozitsiya o'zgarishi animatsiyasi | Animatsiyali saralash va filtratsiya uchun ishlating (Compose 1.7+). |
// contentType va prefetch bilan optimallashtirilgan LazyColumn
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
val listState = rememberLazyListState()
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
beyondBoundsPageCount = 2 // prefetch 2 ekran
) {
items(
items = items,
key = { it.id },
contentType = { it.type } // turlarga ko'ra bo'lish
) { item ->
when (item) {
is FeedItem.Post -> PostView(item)
is FeedItem.Ad -> AdView(item)
is FeedItem.Suggested -> SuggestedView(item)
}
}
}
}
// listState orqali unumdorlikni o'lchash
@Composable
fun ScrollPerformance(listState: LazyListState) {
val scrollInfo = listState.layoutInfo
val visibleCount = scrollInfo.visibleItemsInfo.size
val total = scrollInfo.totalItemsCount
// derivedStateOf — qiymat o'zgarmasa qayta tuzilmaydi
val scrollProgress by remember {
derivedStateOf {
if (total > 0) visibleCount.toFloat() / total else 0f
}
}
LinearProgressIndicator(
progress = scrollProgress,
modifier = Modifier.fillMaxWidth().height(2.dp)
)
}
Keng tarqalgan xatolar: (1) key yo'qligi — elementlar ro'yxat har o'zgarishida qayta kompozitsiya qilinadi; (2) snapshot oqimlarisiz mutableStateListOf ishlatish — o'zgarishlar kuzatilmasligi mumkin; (3) itemContent ichida suspend-funksiyalarni chaqirish — kompozitsiyani to'xtatadi; (4) aralash turlar uchun contentType yo'qligi — Compose matn posti uchun reklama kompozitsiyasini qayta ishlatadi, vizual artefaktlarga sabab bo'ladi. IT Sectr-da biz uch va undan ortiq kontent turiga ega barcha ro'yxatlarga contentType qo'shamiz.
Tez-tez beriladigan savollar
Uch parametrni tekshiring: (1) key — barqaror kalitsiz Compose kompozitsiyani qayta ishlata olmaydi, (2) contentType — turli element turlari bo'lgan ro'yxatlar uchun, (3) beyondBoundsPageCount — rasmlar uchun 2 gacha oshiring. itemContent ichidagi og'ir hisoblashlarni ma'lumot kaliti bilan remember-ga ko'chiring. Oddiy grafika uchun Image o'rniga Modifier.drawWithContent ishlating.
Elementlar orasidagi bo'shliqlar uchun LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) ishlating. Chiziqli vizual ajratgich uchun items orasiga item { Divider() } qo'shing. Avtomatik ajratgich: items(items, key = { it.id }) { item -> ... }, va har bir element orasiga item { HorizontalDivider() } kiriting. Buning uchun LazyListScope.items(list, key) { /* element */ } va alohida LazyListScope.item { Divider() } ishlating.
LazyColumn standartda cheksiz balandlikka (fillMaxSize) ega. Balandligi sobit bo'lmagan Column ichida LazyColumn o'lchamni aniqlay olmaydi. Yechim: (1) LazyColumn-ga sobit balandlik bering: Modifier.height(400.dp), (2) Column ichida Modifier.weight(1f) ishlating, (3) LazyColumn-ni vertikal aylanadigan konteynerga joylashtirmang — LazyColumn-ni header va footer uchun item {} bilan ildiz element sifatida ishlating.
LazyColumn State o'zgarishlariga avtomatik munosabat bildiradi. Agar ma'lumot mutableStateListOf yoki mutableStateOf da saqlansa, Compose faqat o'zgargan elementlarni qayta kompozitsiya qiladi. ViewModel-dan ro'yxatlar uchun Flow bilan collectAsState() ishlating. Key bilan ro'yxat yangilanganda, Compose o'zgarishlarni (qo'shish, o'chirish, ko'chirish) Modifier.animateItemPlacement() orqali avtomatik animatsiya qiladi.
LazyColumn — bir ustunli ro'yxatlar uchun (lenta, chat, sharhlar). LazyVerticalGrid — panjaralar uchun (galereya, katalog, ikonkalar). Agar elementlar telefonda bir ustunda, planshetda esa ikki ustunda bo'lishi kerak bo'lsa — GridCells.Adaptive bilan LazyVerticalGrid ishlating. Agar aralash panjara (bir bo'limda turli xil ustunlar soni) kerak bo'lsa — LazyColumn ishlating va items ichida gorizontal LazyRow yoki ichma-ich LazyVerticalGrid joylashtiring.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.