LazyColumn — Jetpack Compose-un gecikməli yükləmə ilə sürüşdürülə bilən siyahıları göstərmək üçün komponenti. RecyclerView-dən fərqli olaraq, LazyColumn ViewHolder yaratmır və XML istifadə etmir — bütün elementlər composable-funksiyalar vasitəsilə deklarativ şəkildə təsvir olunur. LazyColumn-un nə olduğunu, LazyListState və items API-nin necə işlədiyini və 60 FPS səviyyəsində performansa necə nail olmağı göstəririk. IT Sectr-də LazyColumn-dan bütün yeni Compose layihələrində istifadə edirik. Klassik yanaşma ilə müqayisə üçün RecyclerView haqqında məqaləni oxuyun.
Əsas məqamlar
items(), itemsIndexed(), items() funksiyaları.LazyColumn — Foundation Compose kitabxanasından olan, gecikməli (lazy) yükləmə ilə şaquli element siyahısını göstərən composable-funksiyadır: yalnız ekranda görünən və ya yaxınlıqda olan (prefetch zonası) elementlər yaradılır və kompozisiya olunur. LazyColumn Compose 1.0 (2021) ilə birlikdə LazyRow (üfüqi siyahı) və LazyVerticalGrid (şəbəkə) ilə birlikdə ortaya çıxdı.
Gecikməli yükləmə SubcomposeLayout mexanizmi vasitəsilə işləyir: LazyColumn mövcud yeri ölçür, LayoutInfo-dan görünən diapazonu tələb edir və yalnız bu diapazondakı elementləri kompozisiya edir. Ekrandan çıxan elementlər kompozisiyanı tərk edir (prefetch buferi istisna olmaqla). Google məlumatlarına görə (Android Performance, 2026), LazyColumn orta seqment cihazlarında 10000+ elementdən ibarət siyahını sürüşdürərkən 60 FPS təmin edir.
Column şaquli sürüşdürmə ilə — verticalScroll modifierinin tətbiqi ilə bütün elementlərin bir-birinin altında adi yerləşdirilməsidir. Column gecikməli deyil: bütün uşaq elementlər görünməsələr belə dərhal kompozisiya olunur. 200+ elementdən ibarət siyahı üçün Column başlanğıcda əhəmiyyətli ləngiməyə səbəb olur. LazyColumn yalnız görünənləri + buferi (standart olaraq 1 ekran irəli/geri) kompozisiya edir, bu da onu uzun siyahılar üçün əvəzolunmaz edir.
items API — LazyColumn üçün genişləndirmə funksiyaları dəsti, məlumat siyahısını və hər bir element üçün DSL təsvirini qəbul edir. Əsas funksiyalar: sabit say üçün items(count, key, itemContent), siyahı üçün items(list, key, itemContent), indekslə itemsIndexed(list, key, itemContent). key parametri — performans üçün məcburi şərt — Compose-a elementin sabit identifikatorunu verir.
// itemContent ilə əsas LazyColumn
@Composable
fun ArticleList(articles: List<Article>) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// Header — gecikməli deyil, həmişə başlanğıcda
item {
Text("Son məqalələr", style = MaterialTheme.typography.headlineMedium)
}
// Məqalələr siyahısı id ilə açar
items(articles, key = { it.id }) { article ->
ArticleCard(
title = article.title,
summary = article.summary,
onClick = { onArticleClick(article.id) }
)
}
// Yükləmə göstəricisi olan Footer
item {
CircularProgressIndicator(
modifier = Modifier.fillMaxWidth().padding(16.dp)
)
}
}
}
// Fərdi siyahı 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(): siyahıda həmişə mövcud olan tək elementlər (header, footer, ayırıcılar) üçün item {} istifadə edin. Dinamik məlumat siyahısı üçün items() istifadə edin. items() Iterable və ya Int və hər bir element üçün çağırılan DSL bloku qəbul edir. Bir LazyColumn-da item və items-i birləşdirin: çağırış sırası göstərilmə sırasını müəyyən edir.
LazyListState — siyahı vəziyyətini saxlayan obyekt: cari sürüşdürmə mövqeyi (firstVisibleItemIndex, firstVisibleItemScrollOffset), görünən elementlər (layoutInfo) və proqram sürüşdürmə metodları (scrollToItem, animateScrollToItem). LazyListState rememberLazyListState() vasitəsilə yaradılır və state parametri ilə LazyColumn-a ötürülür.
// Sürüşdürmə mövqeyini qoruyan 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)
}
}
// "Yuxarı" düyməsi 10-cu elementdən sonra görünür
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, "Yuxarı qalx")
}
}
}
}
// Görünən elementlər haqqında məlumat almaq
@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 cəmi $totalItems-dən göstərilir")
}
Proqram sürüşdürməsi listState-in suspend-funksiyaları vasitəsilə yerinə yetirilir: scrollToItem(index, scrollOffset) — ani hərəkət (animasiyasız), animateScrollToItem(index) — animasiya ilə. Korutindən sürüşdürmək üçün LaunchedEffect və ya coroutineScope.launch istifadə edin. Vacib: kompozisiyadan (içəridə @Composable) suspend-funksiyaları çağırmayın — lambdalardan (onClick, LaunchedEffect) istifadə edin.
LazyColumn və RecyclerView eyni vəzifəni həll edir — uzun siyahıların effektiv göstərilməsi. Fərq arxitekturadadır: RecyclerView ViewHolder və Adapter (klassik Android View) istifadə edir, LazyColumn — XML və ViewHolder olmadan deklarativ composable-funksiyalar. Seçim layihənin texnologiya stackindən asılıdır: Compose vs View-based UI.
| Parametr | LazyColumn (Compose) | RecyclerView (View) |
|---|---|---|
| Layout | Composable-funksiyalar (Kotlin DSL) | XML faylları + ViewBinding |
| Adapter | items() / itemsIndexed() DSL | RecyclerView.Adapter + ViewHolder |
| Çeşidləmə/filtrasiya | Snapshot state + derivedStateOf | DiffUtil + AsyncListDiffer |
| Animasiya | AnimatedVisibility + Modifier.animateItem | DefaultItemAnimator |
| Müxtəlif element tipləri | item {}/items() + tipə görə when | getItemViewType + çoxsaylı ViewHolder-lər |
| Mövqeyə sürüşdürmə | listState.animateScrollToItem() | layoutManager.scrollToPosition() |
| Prefetch | In-process (SubcomposeLayout buferi) | RecycledViewPool + GapWorker |
| Şəbəkə/paginasıya | collectAsLazyPagingItems() + Paging 3 | PagingDataAdapter + Paging 3 |
Google tövsiyəsi (Android Developers, 2026): Jetpack Compose-da yeni layihələr üçün LazyColumn istifadə edin. View-based layihələr üçün RecyclerView istifadə edin. LazyColumn Compose tələb edir, bu da APK-ya təxminən 3–5 MB əlavə edir — köhnə cihazları dəstəkləyərkən bunu nəzərə alın. Əgər layihə artıq View sistemindən istifadə edirsə, LazyColumn AndroidView olmadan XML layoutu ilə eyni ekranda mövcud ola bilməz — belə hallarda RecyclerView-də qalmaq daha asandır.
LazyVerticalGrid — sabit sayda sütunları (columns) olan şəbəkələr üçün LazyColumn versiyası. LazyHorizontalGrid — sabit sayda sətirləri (rows) olan üfüqi şəbəkə. Hər iki komponent LazyColumn ilə eyni gecikməli yükləmə prinsiplərindən və eyni items API-dən istifadə edir. columns parametri GridCells.Fixed(N) və ya GridCells.Adaptive(minSize) vasitəsilə sütunların sayını təyin edir.
// Adaptiv sütunlarla şəbəkə
@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)
)
}
}
}
// Üfüqi siyahı (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 hüceyrənin minimum ölçüsünə əsasən sütunların sayını avtomatik hesablayır. Məsələn, GridCells.Adaptive(128.dp) 360 dp enində ekranda 2 sütun, 600 dp planşetdə 4 sütun yerləşdirir. GridCells.Fixed(2) həmişə düz 2 sütun göstərir. Adaptive çoxekranlı adaptasiya üçün daha üstündür — media sorğuları olmadan ekran eninə avtomatik uyğunlaşır.
Performans LazyColumn üç amildən asılıdır: açarların sabitliyi (key), məzmun növü (contentType) və prefetch buferi (beyondBounds). Standart olaraq LazyColumn 1 ekran irəli və 1 geri buferləyir. Google (Android Developers, 2026) bu parametrləri müxtəlif element tipləri və ya mürəkkəb kompozisiyası olan siyahılar üçün konfiqurasiya etməyi tövsiyə edir.
| Parametr | Təsvir | Tövsiyə |
|---|---|---|
| key | Elementin sabit identifikatoru | Həmişə key = { it.id } təyin edin. Key olmadan Compose indeksdən istifadə edir və elementlərin dəyişdirilməsi animasiyanı pozur. |
| contentType | Hovuzların bölünməsi üçün məzmun növü | Müxtəlif element tipləri olan siyahılar üçün təyin edin (mətn + şəkil + reklam). Compose yalnız bir contentType daxilində kompozisiyanı təkrar istifadə edəcək. |
| beyondBounds | Prefetch üçün ekranların sayı | Ağır şəkilləri olan siyahılar üçün 2–3-ə qədər artırın. Standart olaraq 1 ekran. |
| Modifier.animateItem | Mövqe dəyişmə animasiyası | Animasiyalı çeşidləmə və filtrasiya üçün istifadə edin (Compose 1.7+). |
// contentType və prefetch ilə optimallaşdırılmış 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 } // növlərə görə bölmə
) { item ->
when (item) {
is FeedItem.Post -> PostView(item)
is FeedItem.Ad -> AdView(item)
is FeedItem.Suggested -> SuggestedView(item)
}
}
}
}
// listState vasitəsilə performansın ölçülməsi
@Composable
fun ScrollPerformance(listState: LazyListState) {
val scrollInfo = listState.layoutInfo
val visibleCount = scrollInfo.visibleItemsInfo.size
val total = scrollInfo.totalItemsCount
// derivedStateOf — dəyər dəyişməyibsə yenidən tərtib etmir
val scrollProgress by remember {
derivedStateOf {
if (total > 0) visibleCount.toFloat() / total else 0f
}
}
LinearProgressIndicator(
progress = scrollProgress,
modifier = Modifier.fillMaxWidth().height(2.dp)
)
}
Ümumi səhvlər: (1) key-in olmaması — elementlər siyahının hər dəyişməsində yenidən kompozisiya olunur; (2) snapshot axınları olmadan mutableStateListOf istifadəsi — dəyişikliklər izlənilməyə bilər; (3) itemContent daxilində suspend-funksiyaların çağırılması — kompozisiyanı kəsir; (4) qarışıq tiplər üçün contentType olmaması — Compose mətn postu üçün reklam kompozisiyasını təkrar istifadə edir, vizual artefaktlara səbəb olur. IT Sectr-də üç və daha çox məzmun növü olan bütün siyahılara contentType əlavə edirik.
Tez-tez verilən suallar
Üç parametri yoxlayın: (1) key — sabit açar olmadan Compose kompozisiyanı təkrar istifadə edə bilməz, (2) contentType — müxtəlif element tipləri olan siyahılar üçün, (3) beyondBoundsPageCount — şəkillər üçün 2-yə qədər artırın. itemContent daxilində ağır hesablamaları məlumat açarı ilə remember-ə köçürün. Sadə qrafika üçün Image əvəzinə Modifier.drawWithContent istifadə edin.
Elementlər arasında boşluqlar üçün LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) istifadə edin. Xətt ilə vizual ayırıcı üçün items arasında item { Divider() } əlavə edin. Avtomatik ayırıcı: items(items, key = { it.id }) { item -> ... }, hər bir element arasında item { HorizontalDivider() } daxil edin. Bunun üçün LazyListScope.items(list, key) { /* element */ } və ayrıca LazyListScope.item { Divider() } istifadə edin.
LazyColumn standart olaraq sonsuz hündürlüyə malikdir (fillMaxSize). Sabit hündürlüyü olmayan Column içərisində LazyColumn ölçüsünü təyin edə bilməz. Həll yolu: (1) LazyColumn-a sabit hündürlük təyin edin: Modifier.height(400.dp), (2) Column daxilində Modifier.weight(1f) istifadə edin, (3) LazyColumn-u şaquli sürüşdürülən konteynerə yerləşdirməyin — LazyColumn-u header və footer üçün item {} ilə kök element kimi istifadə edin.
LazyColumn State dəyişikliklərinə avtomatik reaksiya verir. Məlumat mutableStateListOf və ya mutableStateOf-də saxlanılırsa, Compose yalnız dəyişmiş elementləri yenidən kompozisiya edir. ViewModel-dən siyahılar üçün Flow ilə collectAsState() istifadə edin. Key ilə siyahı yenilənərkən Compose Modifier.animateItemPlacement() vasitəsilə dəyişiklikləri (əlavə etmə, silmə, daşıma) avtomatik animasiya edir.
LazyColumn — bir sütunlu siyahılar üçün (lent, söhbət, şərhlər). LazyVerticalGrid — şəbəkələr üçün (qalereya, kataloq, ikonlar). Elementlər telefonda bir sütunda, planşetdə isə iki sütunda olmalıdırsa — GridCells.Adaptive ilə LazyVerticalGrid istifadə edin. Qarışıq şəbəkə (bir bölmədə müxtəlif sayda sütunlar) lazımdırsa — LazyColumn istifadə edin və items daxilində üfüqi LazyRow və ya iç-içə LazyVerticalGrid yerləşdirin.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun