LazyColumn — Jetpack Compose-এর একটি উপাদান যা অলস লোডিং সহ স্ক্রোলযোগ্য তালিকা প্রদর্শন করে। RecyclerView-এর বিপরীতে, LazyColumn ViewHolders তৈরি করে না এবং XML ব্যবহার করে না — সমস্ত উপাদান composable ফাংশনের মাধ্যমে ঘোষণামূলকভাবে বর্ণনা করা হয়। আমরা দেখাই LazyColumn কী, LazyListState এবং items API কীভাবে কাজ করে, এবং কীভাবে 60 FPS কর্মক্ষমতা অর্জন করা যায়। IT Sectr-এ আমরা সব নতুন Compose প্রকল্পে LazyColumn ব্যবহার করি। ক্লাসিক পদ্ধতির সাথে তুলনার জন্য, RecyclerView সম্পর্কে নিবন্ধ পড়ুন।
মুখ্য বিষয়
items(), itemsIndexed(), items() ফাংশন।LazyColumn Foundation Compose লাইব্রেরির একটি composable ফাংশন যা অলস লোডিং সহ উপাদানের একটি উল্লম্ব তালিকা প্রদর্শন করে: শুধুমাত্র স্ক্রিনে দৃশ্যমান বা কাছাকাছি উপাদান (prefetch জোন) তৈরি এবং রচিত হয়। LazyColumn Compose 1.0 (2021)-এ LazyRow (অনুভূমিক তালিকা) এবং LazyVerticalGrid (গ্রিড) সহ আবির্ভূত হয়েছিল।
অলস লোডিং SubcomposeLayout প্রক্রিয়ার মাধ্যমে কাজ করে: LazyColumn উপলব্ধ স্থান পরিমাপ করে, LayoutInfo থেকে দৃশ্যমান পরিসর অনুরোধ করে এবং শুধুমাত্র সেই পরিসরে উপাদান রচনা করে। স্ক্রিনের বাইরে চলে যাওয়া উপাদানগুলি কম্পোজিশন ত্যাগ করে (prefetch বাফার ব্যতীত)। Google (Android Performance, 2026) অনুসারে, LazyColumn মধ্যম-স্তরের ডিভাইসে 10000+ উপাদানের তালিকা স্ক্রোল করার সময় 60 FPS বজায় রাখে।
Column উল্লম্ব স্ক্রোল সহ — verticalScroll মডিফায়ার প্রয়োগ করে সব উপাদানের একের নীচে এক সাধারণ বিন্যাস। Column অলস নয়: সমস্ত চাইল্ড উপাদান তাৎক্ষণিকভাবে রচিত হয়, এমনকি যদি তারা দৃশ্যমান না হয়। 200+ উপাদানের তালিকার জন্য, Column স্টার্টআপে উল্লেখযোগ্য ব্যবধান সৃষ্টি করে। LazyColumn শুধুমাত্র দৃশ্যমান উপাদান + বাফার (ডিফল্টরূপে 1 স্ক্রিন সামনে/পিছনে) রচনা করে, যা এটিকে দীর্ঘ তালিকার জন্য অপরিহার্য করে তোলে।
items API LazyColumn-এর জন্য এক্সটেনশন ফাংশনের একটি সেট যা একটি ডেটা তালিকা এবং প্রতিটি উপাদানের জন্য DSL বর্ণনাকারী গ্রহণ করে। প্রধান ফাংশনগুলি হল: নির্দিষ্ট সংখ্যার জন্য items(count, key, itemContent), তালিকার জন্য items(list, key, itemContent), সূচক সহ itemsIndexed(list, key, itemContent)। key প্যারামিটার কর্মক্ষমতার জন্য বাধ্যতামূলক — এটি Compose-কে একটি স্থিতিশীল উপাদান সনাক্তকারী দেয়।
// itemContent সহ বেসিক LazyColumn
@Composable
fun ArticleList(articles: List<Article>) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// Header — অলস নয়, সর্বদা শুরুতে
item {
Text("সর্বশেষ নিবন্ধ", style = MaterialTheme.typography.headlineMedium)
}
// id দ্বারা key সহ নিবন্ধ তালিকা
items(articles, key = { it.id }) { article ->
ArticleCard(
title = article.title,
summary = article.summary,
onClick = { onArticleClick(article.id) }
)
}
// লোডিং নির্দেশক সহ Footer
item {
CircularProgressIndicator(
modifier = Modifier.fillMaxWidth().padding(16.dp)
)
}
}
}
// কাস্টম তালিকা উপাদান
@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 {} বনাম items(): একক উপাদানের জন্য item {} ব্যবহার করুন যা তালিকায় সর্বদা উপস্থিত থাকে (হেডার, ফুটার, বিভাজক)। গতিশীল ডেটা তালিকার জন্য items() ব্যবহার করুন। items() একটি Iterable বা Int এবং একটি DSL ব্লক গ্রহণ করে যা প্রতিটি উপাদানের জন্য কল করা হয়। একটি LazyColumn-এ item এবং items একত্রিত করুন: কলের ক্রম প্রদর্শনের ক্রম নির্ধারণ করে।
LazyListState একটি বস্তু যা তালিকার অবস্থা সংরক্ষণ করে: বর্তমান স্ক্রোল অবস্থান (firstVisibleItemIndex, firstVisibleItemScrollOffset), দৃশ্যমান উপাদান (layoutInfo), এবং প্রোগ্রামেটিক স্ক্রোলিং পদ্ধতি (scrollToItem, animateScrollToItem)। LazyListState rememberLazyListState()-এর মাধ্যমে তৈরি করা হয় এবং state প্যারামিটারের মাধ্যমে LazyColumn-এ পাঠানো হয়।
// স্ক্রোল অবস্থান সংরক্ষণ সহ 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)
}
}
// "উপরে" বোতাম 10ম উপাদানের পরে প্রদর্শিত হয়
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, "উপরে যান")
}
}
}
}
// দৃশ্যমান উপাদান সম্পর্কে তথ্য পাওয়া
@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 দেখানো হচ্ছে $totalItems-এর মধ্যে")
}
প্রোগ্রামেটিক স্ক্রোলিং listState-এর suspend ফাংশনের মাধ্যমে সঞ্চালিত হয়: scrollToItem(index, scrollOffset) — তাৎক্ষণিক গতি (অ্যানিমেশন ছাড়া), animateScrollToItem(index) — অ্যানিমেশন সহ। করুটিন থেকে স্ক্রোল করার জন্য, LaunchedEffect বা coroutineScope.launch ব্যবহার করুন। গুরুত্বপূর্ণ: কম্পোজিশন থেকে (@Composable-এর ভিতর) suspend ফাংশন কল করবেন না — ল্যাম্বডা (onClick, LaunchedEffect) ব্যবহার করুন।
LazyColumn এবং RecyclerView একই সমস্যা সমাধান করে — দীর্ঘ তালিকার কার্যকরী প্রদর্শন। পার্থক্যটি আর্কিটেকচারে: RecyclerView ViewHolder এবং Adapter (ক্লাসিক Android View) ব্যবহার করে, LazyColumn XML বা ViewHolder ছাড়া ঘোষণামূলক composable ফাংশন ব্যবহার করে। পছন্দ প্রকল্পের প্রযুক্তি স্ট্যাকের উপর নির্ভর করে: Compose বনাম View-ভিত্তিক UI।
| প্যারামিটার | LazyColumn (Compose) | RecyclerView (View) |
|---|---|---|
| লেআউট | Composable ফাংশন (Kotlin DSL) | XML ফাইল + ViewBinding |
| অ্যাডাপ্টার | items() / itemsIndexed() DSL | RecyclerView.Adapter + ViewHolder |
| সর্টিং/ফিল্টারিং | Snapshot state + derivedStateOf | DiffUtil + AsyncListDiffer |
| অ্যানিমেশন | AnimatedVisibility + Modifier.animateItem | DefaultItemAnimator |
| বিভিন্ন উপাদানের ধরন | item {}/items() + প্রকার অনুযায়ী when | getItemViewType + একাধিক ViewHolders |
| অবস্থানে স্ক্রোল | listState.animateScrollToItem() | layoutManager.scrollToPosition() |
| Prefetch | ইন-প্রসেস (SubcomposeLayout বাফার) | RecycledViewPool + GapWorker |
| নেটওয়ার্ক/পেজিনেশন | collectAsLazyPagingItems() + Paging 3 | PagingDataAdapter + Paging 3 |
Google-এর সুপারিশ (Android Developers, 2026): Jetpack Compose-এ নতুন প্রকল্পের জন্য, LazyColumn ব্যবহার করুন। View-ভিত্তিক প্রকল্পের জন্য, RecyclerView ব্যবহার করুন। LazyColumn-এর Compose প্রয়োজন, যা APK-তে প্রায় 3–5 MB যোগ করে — পুরানো ডিভাইস সমর্থন করার সময় এটি বিবেচনা করুন। যদি একটি প্রকল্প ইতিমধ্যে View সিস্টেম ব্যবহার করে, LazyColumn AndroidView ছাড়া একটি স্ক্রিনে XML লেআউটের সাথে সহ-অবস্থান করতে পারে না — এই ধরনের ক্ষেত্রে, RecyclerView-এর সাথেই থাকা সহজ।
LazyVerticalGrid নির্দিষ্ট সংখ্যক কলাম সহ গ্রিডের জন্য LazyColumn-এর একটি সংস্করণ। LazyHorizontalGrid নির্দিষ্ট সারি সহ একটি অনুভূমিক গ্রিড। উভয় উপাদান LazyColumn-এর মতো একই অলস লোডিং নীতি এবং একই items API ব্যবহার করে। columns প্যারামিটার GridCells.Fixed(N) বা GridCells.Adaptive(minSize)-এর মাধ্যমে কলামের সংখ্যা নির্ধারণ করে।
// অভিযোজিত কলাম সহ গ্রিড
@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)
)
}
}
}
// অনুভূমিক তালিকা (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 ন্যূনতম সেল আকারের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে কলামের সংখ্যা গণনা করে। উদাহরণস্বরূপ, GridCells.Adaptive(128.dp) 360 dp চওড়া স্ক্রিনে 2 কলাম রাখে, 600 dp ট্যাবলেটে — 4 কলাম। GridCells.Fixed(2) সর্বদা ঠিক 2 কলাম দেখায়। Adaptive মাল্টি-স্ক্রিন অভিযোজনের জন্য পছন্দনীয় — এটি মিডিয়া ক্যোয়ারী ছাড়াই স্বয়ংক্রিয়ভাবে স্ক্রিন প্রস্থে সামঞ্জস্য হয়।
LazyColumn-এর কর্মক্ষমতা তিনটি বিষয়ের উপর নির্ভর করে: কী স্থিতিশীলতা (key), বিষয়বস্তুর ধরন (contentType), এবং prefetch বাফার (beyondBounds)। ডিফল্টরূপে, LazyColumn 1 স্ক্রিন সামনে এবং 1 পিছনে বাফার করে। Google (Android Developers, 2026) বিভিন্ন উপাদানের ধরন বা জটিল কম্পোজিশন সহ তালিকার জন্য এই প্যারামিটারগুলি কনফিগার করার সুপারিশ করে।
| প্যারামিটার | বিবরণ | সুপারিশ |
|---|---|---|
| key | স্থিতিশীল উপাদান সনাক্তকারী | সর্বদা key = { it.id } নির্দিষ্ট করুন। key ছাড়া, Compose সূচক ব্যবহার করে, এবং উপাদান পুনর্বিন্যাস অ্যানিমেশন ভেঙে দেয়। |
| contentType | পুল পৃথকীকরণের জন্য বিষয়বস্তুর ধরন | বিভিন্ন উপাদানের ধরন (টেক্সট + ছবি + বিজ্ঞাপন) সহ তালিকার জন্য নির্দিষ্ট করুন। Compose শুধুমাত্র একই contentType-এর মধ্যে কম্পোজিশন পুনরায় ব্যবহার করবে। |
| beyondBounds | prefetch স্ক্রিনের সংখ্যা | ভারী ছবি সহ তালিকার জন্য 2–3 পর্যন্ত বাড়ান। ডিফল্ট 1 স্ক্রিন। |
| Modifier.animateItem | অবস্থান পরিবর্তন অ্যানিমেশন | অ্যানিমেটেড সর্টিং এবং ফিল্টারিংয়ের জন্য ব্যবহার করুন (Compose 1.7+)। |
// contentType এবং prefetch সহ অপ্টিমাইজড LazyColumn
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
val listState = rememberLazyListState()
LazyColumn(
state = listState,
modifier = Modifier.fillMaxSize(),
beyondBoundsPageCount = 2 // 2 স্ক্রিনের prefetch
) {
items(
items = items,
key = { it.id },
contentType = { it.type } // প্রকার অনুযায়ী বিভাজন
) { item ->
when (item) {
is FeedItem.Post -> PostView(item)
is FeedItem.Ad -> AdView(item)
is FeedItem.Suggested -> SuggestedView(item)
}
}
}
}
// listState-এর মাধ্যমে কর্মক্ষমতা পরিমাপ
@Composable
fun ScrollPerformance(listState: LazyListState) {
val scrollInfo = listState.layoutInfo
val visibleCount = scrollInfo.visibleItemsInfo.size
val total = scrollInfo.totalItemsCount
// derivedStateOf — মান পরিবর্তন না হলে পুনরায় রচনা করে না
val scrollProgress by remember {
derivedStateOf {
if (total > 0) visibleCount.toFloat() / total else 0f
}
}
LinearProgressIndicator(
progress = scrollProgress,
modifier = Modifier.fillMaxWidth().height(2.dp)
)
}
সাধারণ ভুল: (1) key-এর অভাব — যেকোনো তালিকা পরিবর্তনে উপাদান পুনরায় রচিত হয়; (2) স্ন্যাপশট ফ্লো ছাড়া mutableStateListOf ব্যবহার — পরিবর্তন ট্র্যাক নাও হতে পারে; (3) itemContent-এর ভিতর suspend ফাংশন কল করা — কম্পোজিশন বাধাগ্রস্ত করে; (4) মিশ্র প্রকারের জন্য contentType নেই — Compose টেক্সট পোস্টের জন্য বিজ্ঞাপনের কম্পোজিশন পুনরায় ব্যবহার করে, যার ফলে দৃশ্য কলাকৌশল হয়। IT Sectr-এ আমরা তিন বা ততোধিক বিষয়বস্তুর প্রকার সহ সব তালিকায় contentType যোগ করি।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
তিনটি প্যারামিটার পরীক্ষা করুন: (1) key — স্থিতিশীল কী ছাড়া, Compose কম্পোজিশন পুনরায় ব্যবহার করতে পারে না, (2) contentType — বিভিন্ন উপাদানের ধরনের তালিকার জন্য, (3) beyondBoundsPageCount — ছবির জন্য 2 পর্যন্ত বাড়ান। itemContent-এর ভিতর ভারী গণনা ডেটা দ্বারা কী সহ remember-এ সরান। সরল গ্রাফিক্সের জন্য Image-এর পরিবর্তে Modifier.drawWithContent ব্যবহার করুন।
উপাদানের মধ্যে স্থানের জন্য LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) ব্যবহার করুন। রেখা সহ দৃশ্য বিভাজকের জন্য, items-এর মধ্যে item { Divider() } যোগ করুন। স্বয়ংক্রিয় বিভাজক: items(items, key = { it.id }) { item -> ... }, এবং প্রতিটি উপাদানের মধ্যে item { HorizontalDivider() } ঢোকান। এর জন্য, LazyListScope.items(list, key) { /* উপাদান */ } এবং পৃথকভাবে LazyListScope.item { Divider() } ব্যবহার করুন।
LazyColumn-এর ডিফল্টরূপে অসীম উচ্চতা (fillMaxSize) থাকে। নির্দিষ্ট উচ্চতা ছাড়া Column-এর ভিতর, LazyColumn তার আকার নির্ধারণ করতে পারে না। সমাধান: (1) LazyColumn-এ একটি নির্দিষ্ট উচ্চতা সেট করুন: Modifier.height(400.dp), (2) Column-এর ভিতর Modifier.weight(1f) ব্যবহার করুন, (3) LazyColumn-কে উল্লম্বভাবে স্ক্রোলযোগ্য কন্টেইনারের ভিতর নেস্ট করবেন না — হেডার এবং ফুটারের জন্য item {} সহ LazyColumn-কে রুট উপাদান হিসাবে ব্যবহার করুন।
LazyColumn স্বয়ংক্রিয়ভাবে State পরিবর্তনে প্রতিক্রিয়া জানায়। যদি ডেটা mutableStateListOf বা mutableStateOf-এ সংরক্ষিত থাকে, Compose শুধুমাত্র পরিবর্তিত উপাদান পুনরায় রচনা করে। ViewModel থেকে তালিকার জন্য, Flow-এর সাথে collectAsState() ব্যবহার করুন। key সহ তালিকা আপডেট করার সময়, Compose স্বয়ংক্রিয়ভাবে Modifier.animateItemPlacement()-এর মাধ্যমে পরিবর্তনগুলি (যোগ, অপসারণ, পুনর্বিন্যাস) অ্যানিমেট করে।
LazyColumn — একক-কলাম তালিকার জন্য (ফিড, চ্যাট, মন্তব্য)। LazyVerticalGrid — গ্রিডের জন্য (গ্যালারি, ক্যাটালগ, আইকন)। যদি উপাদান ফোনে এক কলাম এবং ট্যাবলেটে দুই কলামে হওয়া উচিত — GridCells.Adaptive সহ LazyVerticalGrid ব্যবহার করুন। যদি মিশ্র গ্রিডের প্রয়োজন হয় (এক বিভাগে বিভিন্ন কলাম গণনা) — LazyColumn ব্যবহার করুন এবং items-এর ভিতর অনুভূমিক LazyRow বা নেস্টেড LazyVerticalGrid ঢোকান।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন