অনেক অ্যাপে আইটেমের কালেকশন দেখাতে হয়। Jetpack Compose-এ কীভাবে আপনি দক্ষতার সাথে এটি করতে পারবেন তা এই ডকুমেন্টে ব্যাখ্যা করা হয়েছে।
আপনি যদি জানেন যে আপনার ব্যবহারের ক্ষেত্রে কোনও স্ক্রল করার প্রয়োজন নেই, তাহলে আপনি
একটি সাধারণ Column বা Row (নির্দেশের উপর নির্ভর করে) ব্যবহার করতে এবং নিম্নলিখিত উপায়ে একটি তালিকার
উপর আইটেরেশন করে প্রতিটি আইটেমের কন্টেন্ট এমিট করতে পারেন:
@Composable fun MessageList(messages: List<Message>) { Column { messages.forEach { message -> MessageRow(message) } } }
আমরা verticalScroll() মডিফায়ার ব্যবহার করে Column-কে স্ক্রল করা যায় এমন করে তুলতে পারি।
লেজি তালিকা
আপনাকে যদি অনেক আইটেম (বা অজানা দৈর্ঘ্যের তালিকা) দেখাতে হয়, তাহলে
Column-এর মতো লেআউট ব্যবহার করলে পারফর্ম্যান্স সংক্রান্ত সমস্যা হতে পারে, কারণ সব আইটেম কম্পোজ করা হবে এবং লেআউট করা হবে, সেগুলি দেখা যাক বা না যাক।
Compose, কম্পোনেন্টগুলির একটি সেট প্রদান করে যা শুধুমাত্র কম্পোনেন্টের ভিউপোর্টে দৃশ্যমান আইটেমগুলিকে
কম্পোজ ও লেআউট করে। এইসব কম্পোনেন্টের মধ্যে রয়েছে
LazyColumn
এবং
LazyRow.
নাম থেকেই বোঝা যায় যে,
LazyColumn
ও
LazyRow
এর মধ্যে পার্থক্য হল, আইটেমগুলি যে দিক থেকে সাজানো হয় এবং স্ক্রল করা হয়। LazyColumn
উল্লম্বভাবে স্ক্রল করা যায় এমন তালিকা তৈরি করে এবং LazyRow অনুভূমিকভাবে
স্ক্রল করা যায় এমন তালিকা তৈরি করে।
Compose-এর বেশিরভাগ লেআউটের থেকে Lazy কম্পোনেন্ট আলাদা। @Composable কন্টেন্ট ব্লক প্যারামিটার গ্রহণ করার পরিবর্তে, অ্যাপগুলিকে সরাসরি
কম্পোজ করার অনুমতি দেয়, লেজি কম্পোনেন্ট একটি LazyListScope.() ব্লক প্রদান করে। এই
LazyListScope
ব্লকটি একটি DSL অফার করে যা অ্যাপগুলিকে আইটেমের কন্টেন্ট বর্ণনা করার অনুমতি দেয়। তারপর, লেআউট ও স্ক্রল পজিশনের
প্রয়োজন অনুযায়ী প্রতিটি আইটেমের কন্টেন্ট যোগ করার দায়িত্ব Lazy কম্পোনেন্টের।
LazyListScope DSL
LazyListScope-এর DSL লেআউটে আইটেম বর্ণনা করার জন্য
অনেক ফাংশন প্রদান করে। সবচেয়ে প্রাথমিক পর্যায়ে,
item()
একটি আইটেম যোগ করে এবং
items(Int)
একাধিক আইটেম যোগ করে:
LazyColumn { // Add a single item item { Text(text = "First item") } // Add 5 items items(5) { index -> Text(text = "Item: $index") } // Add another single item item { Text(text = "Last item") } }
এছাড়াও, এমন অনেক এক্সটেনশন ফাংশন আছে যা আপনাকে আইটেমের সংগ্রহ যোগ করতে দেয়, যেমন List।
এইসব এক্সটেনশন আমাদের উপরে দেওয়া Column উদাহরণটি সহজেই
মাইগ্রেট করতে দেয়:
/** * import androidx.compose.foundation.lazy.items */ LazyColumn { items(messages) { message -> MessageRow(message) } }
এছাড়াও,
items()
এক্সটেনশন ফাংশনের একটি ভেরিয়েন্ট আছে, যাকে
itemsIndexed()
বলা হয়, যা ইন্ডেক্স প্রদান করে। আরও বিবরণের জন্য
LazyListScope
রেফারেন্স দেখুন।
লেজি গ্রিড
LazyVerticalGrid
এবং
LazyHorizontalGrid
কম্পোজ করার উপযুক্ত আইটেমগুলি গ্রিডে দেখানোর জন্য সহায়তা প্রদান করে। একটি লেজি ভার্টিক্যাল গ্রিড
এর আইটেমগুলিকে উল্লম্বভাবে স্ক্রল করা যায় এমন কন্টেনারে দেখাবে, যা
একাধিক কলাম জুড়ে স্প্যান করা থাকে, অন্যদিকে লেজি হরাইজন্টাল গ্রিড
হরাইজন্টাল অ্যাক্সিসে একই আচরণ করবে।
তালিকার মতো গ্রিডেও একই শক্তিশালী API ক্ষমতা আছে এবং কন্টেন্ট বর্ণনা করার জন্য
খুবই একই রকম DSL -
LazyGridScope.()
ব্যবহার করা হয়।
LazyVerticalGrid
এর মধ্যে থাকা columns প্যারামিটার এবং
LazyHorizontalGrid
এর মধ্যে থাকা rows প্যারামিটার নিয়ন্ত্রণ করে যে কীভাবে সেলগুলি কলাম বা সারিতে ফর্ম করা হয়। নিচের
উদাহরণে, আইটেমগুলিকে গ্রিডে দেখানো হয়েছে। প্রতিটি কলামকে অন্তত 128.dp চওড়া করে সেট করতে
GridCells.Adaptive
ব্যবহার করা হয়েছে:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { items(photos) { photo -> PhotoItem(photo) } }
LazyVerticalGrid আপনাকে আইটেমের প্রস্থ নির্দিষ্ট করতে দেয় এবং তারপরে গ্রিডটি
যতগুলি সম্ভব কলাম ফিট করবে। কলামের সংখ্যা গণনা করার পরে, অবশিষ্ট প্রস্থটি কলামগুলির মধ্যে সমানভাবে
বন্টন করা হয়।
বিভিন্ন স্ক্রিন সাইজে আইটেমের সেট দেখানোর জন্য
সাইজ অ্যাডজাস্ট করার এই অ্যাডাপ্টিভ পদ্ধতিটি বিশেষ উপযোগী।
কতগুলি কলাম ব্যবহার করতে হবে তা আপনি জানলে, তার পরিবর্তে প্রয়োজনীয় কলামের সংখ্যা সহ
GridCells.Fixed
একটি
ইনস্ট্যান্স প্রদান করতে পারেন।
আপনার ডিজাইনে যদি শুধুমাত্র নির্দিষ্ট আইটেমের নন-স্ট্যান্ডার্ড ডাইমেনশন প্রয়োজন হয়,
তাহলে আইটেমের জন্য কাস্টম কলাম স্প্যান প্রদান করতে আপনি গ্রিড সাপোর্ট ব্যবহার করতে পারবেন।
span প্যারামিটারের সাহায্যে কলাম স্প্যান নির্দিষ্ট করুন
LazyGridScope DSL
item এবং items পদ্ধতি।
maxLineSpan,
স্প্যান স্কোপের একটি ভ্যালু, বিশেষ করে তখন কাজে লাগে যখন আপনি
অ্যাডাপ্টিভ সাইজিং ব্যবহার করছেন, কারণ কলামের সংখ্যা নির্দিষ্ট করা থাকে না।
এই উদাহরণে দেখানো হয়েছে কীভাবে সম্পূর্ণ সারি স্প্যান প্রদান করতে হয়:
LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 30.dp) ) { item(span = { // LazyGridItemSpanScope: // maxLineSpan GridItemSpan(maxLineSpan) }) { CategoryCard("Fruits") } // ... }
লেজি স্ট্যাগার্ড গ্রিড
LazyVerticalStaggeredGrid
এবং
LazyHorizontalStaggeredGrid
হল কম্পোজ করার উপযুক্ত এমন আইটেম যা আপনাকে আইটেমের লেজি-লোডেড, স্ট্যাগার্ড গ্রিড তৈরি করতে দেয়।
লেজি ভার্টিক্যাল স্ট্যাগার্ড গ্রিড, একাধিক কলাম জুড়ে থাকা এবং আলাদা আলাদা আইটেমের
উচ্চতা আলাদা হতে পারে এমন একটি উল্লম্বভাবে স্ক্রল করা যায় এমন কন্টেনারে
এর আইটেম দেখায়। লেজি হরাইজন্টাল গ্রিড, আলাদা আলাদা প্রস্থের আইটেম সহ
হরাইজন্টাল অ্যাক্সিসে একই আচরণ করে।
নিচের স্নিপেটটি LazyVerticalStaggeredGrid
ব্যবহার করার একটি প্রাথমিক উদাহরণ, যেখানে 200.dp প্রতিটি আইটেমের জন্য প্রস্থ:
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Adaptive(200.dp), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier .fillMaxWidth() .wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
কলামের নির্দিষ্ট সংখ্যা সেট করতে, আপনি StaggeredGridCells.Adaptive-এর পরিবর্তে
StaggeredGridCells.Fixed(columns) ব্যবহার করতে পারেন।
এটি উপলভ্য প্রস্থকে কলামের সংখ্যা (বা
অনুভূমিক গ্রিডের জন্য সারি) দিয়ে ভাগ করে এবং প্রতিটি আইটেম সেই প্রস্থ (বা
অনুভূমিক গ্রিডের জন্য উচ্চতা) নেয়:
LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Fixed(3), verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), content = { items(randomSizedPhotos) { photo -> AsyncImage( model = photo, contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier .fillMaxWidth() .wrapContentHeight() ) } }, modifier = Modifier.fillMaxSize() )
কন্টেন্ট প্যাডিং
কখনও কখনও কন্টেন্টের চারপাশে প্যাডিং যোগ করতে হবে। lazy
কম্পোনেন্ট আপনাকে কিছু
PaddingValues
contentPadding প্যারামিটারে পাস করার অনুমতি দেয় যাতে এটি কাজ করে:
LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }
এই উদাহরণে, আমরা অনুভূমিক প্রান্তগুলিতে (বাঁদিক ও
ডানদিক) 16.dp প্যাডিং যোগ করি এবং তারপরে কন্টেন্টের উপরে ও নিচে 8.dp যোগ করি।
মনে রাখবেন, এই প্যাডিং কন্টেন্টে প্রয়োগ করা হয়,
LazyColumn-এ নয়। উপরের উদাহরণে, প্রথম আইটেমটির উপরে 8.dp
প্যাডিং যোগ করা হবে, শেষ আইটেমটির নিচে 8.dp যোগ করা হবে এবং সব আইটেমের
বাঁদিকে ও ডানদিকে 16.dp প্যাডিং থাকবে।
আরেকটি উদাহরণ হিসেবে, আপনি Scaffold-এর PaddingValues, LazyColumn-এর contentPadding-এ
পাস করতে পারেন।
এজ-টু-এজ গাইড দেখুন।
কন্টেন্টের স্পেসিং
দুটি আইটেমের মধ্যে স্পেস যোগ করতে, আপনি
Arrangement.spacedBy() ব্যবহার করতে পারেন।
নিচের উদাহরণে প্রতিটি আইটেমের মধ্যে 4.dp স্পেস যোগ করা হয়েছে:
LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
একইভাবে LazyRow-এর জন্য:
LazyRow( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }
তবে, গ্রিড উল্লম্ব ও অনুভূমিক, দুটি বিন্যাসই গ্রহণ করে:
LazyVerticalGrid( columns = GridCells.Fixed(2), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { items(photos) { item -> PhotoItem(item) } }
আইটেম কী
ডিফল্ট হিসেবে, প্রতিটি আইটেমের স্টেটকে
তালিকা বা গ্রিডে আইটেমের পজিশনের সাথে কী হিসেবে সেট করা হয়। তবে, ডেটাসেট পরিবর্তন হলে এটি সমস্যা সৃষ্টি করতে পারে, কারণ
যেসব আইটেমের পজিশন পরিবর্তন হয়, সেগুলি মনে রাখা স্টেট কার্যকরভাবে হারিয়ে ফেলে। আপনি যদি
LazyRow-এর মধ্যে LazyColumn-এর দৃশ্যকল্প কল্পনা করেন, তাহলে সারি পরিবর্তন হলে
আইটেমের পজিশন পরিবর্তন হবে এবং ব্যবহারকারী সারির মধ্যে তার স্ক্রল পজিশন হারিয়ে ফেলবেন।
এটি এড়াতে, আপনি প্রতিটি আইটেমের জন্য একটি স্থিতিশীল এবং অনন্য কী প্রদান করতে পারেন, যা key প্যারামিটারে
একটি ব্লক প্রদান করে। একটি স্থিতিশীল কী প্রদান করলে, ডেটাসেট পরিবর্তন জুড়ে আইটেম স্টেট
সামঞ্জস্যপূর্ণ হতে পারে:
LazyColumn { items( items = messages, key = { message -> // Return a stable + unique key for the item message.id } ) { message -> MessageRow(message) } }
কী প্রদান করার মাধ্যমে, আপনি Compose-কে সঠিকভাবে রিঅর্ডার করা ম্যানেজ করতে সাহায্য করেন। যেমন, আপনার আইটেমে যদি মনে রাখা স্টেট থাকে, তাহলে সেটিং কী, Compose-কে আইটেমের পজিশন পরিবর্তন হলে, এই স্টেটকে আইটেমের সাথে একসাথে সরানোর অনুমতি দেবে।
LazyColumn { items(books, key = { it.id }) { val rememberedValue = remember { Random.nextInt() } } }
তবে, আইটেম কী হিসেবে আপনি কী ধরনের ডেটা ব্যবহার করতে পারবেন, সেই ব্যাপারে একটি সীমাবদ্ধতা আছে।
অ্যাক্টিভিটি আবার তৈরি করার সময়
Bundle, Android-এর মেকানিজম দ্বারা কী-এর ধরনকে
সাপোর্ট করতে হবে। Bundle প্রিমিটিভ,
এনাম বা পার্সেল্যাবল ধরনের ডেটা সাপোর্ট করে।
LazyColumn { items(books, key = { // primitives, enums, Parcelable, etc. }) { // ... } }
Bundle-এর সাথে কীটি মানানসই হতে হবে যাতে অ্যাক্টিভিটি আবার তৈরি করা হলে বা এমনকি
আপনি এই আইটেম থেকে দূরে স্ক্রল করে আবার ফিরে এলে আইটেম কম্পোজ করার মধ্যে থাকা rememberSaveable
ফিরিয়ে আনা যায়।
LazyColumn { items(books, key = { it.id }) { val rememberedValue = rememberSaveable { Random.nextInt() } } }
আইটেম অ্যানিমেশন
আপনি RecyclerView উইজেট ব্যবহার করে থাকলে, আপনি জানবেন যে এটি অটোমেটিক আইটেমের
পরিবর্তন অ্যানিমেট করে।
আইটেম আবার সাজানোর জন্য লেজি লেআউট একই কার্যকারিতা প্রদান করে।
API খুব সহজ - আপনাকে শুধু আইটেম কন্টেন্টে
animateItem
মডিফায়ার সেট করতে হবে:
LazyColumn { // It is important to provide a key to each item to ensure animateItem() works as expected. items(books, key = { it.id }) { Row(Modifier.animateItem()) { // ... } } }
এমনকি, আপনার প্রয়োজন হলে কাস্টম অ্যানিমেশন স্পেসিফিকেশনও প্রদান করতে পারবেন:
LazyColumn { items(books, key = { it.id }) { Row( Modifier.animateItem( fadeInSpec = tween(durationMillis = 250), fadeOutSpec = tween(durationMillis = 100), placementSpec = spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy) ) ) { // ... } } }
আপনার আইটেমের জন্য কী প্রদান করতে ভুলবেন না, যাতে সরানো এলিমেন্টের নতুন পজিশন খুঁজে পাওয়া যায়।
উদাহরণ: লেজি লিস্টে আইটেম অ্যানিমেট করা
Compose-এর সাহায্যে, আপনি লেজি লিস্টে আইটেমের পরিবর্তন অ্যানিমেট করতে পারবেন। একসাথে ব্যবহার করা হলে, নিচের স্নিপেটগুলি অলস তালিকা আইটেম যোগ করা, সরানো এবং পুনর্বিন্যাস করার সময় অ্যানিমেশন প্রয়োগ করে।
আইটেম যোগ করা, সরানো বা আবার সাজানো হলে এই স্নিপেট অ্যানিমেটেড ট্রানজিশন সহ স্ট্রিংয়ের একটি তালিকা দেখায়:
@Composable fun ListAnimatedItems( items: List<String>, modifier: Modifier = Modifier ) { LazyColumn(modifier) { // Use a unique key per item, so that animations work as expected. items(items, key = { it }) { ListItem( headlineContent = { Text(it) }, modifier = Modifier .animateItem( // Optionally add custom animation specs ) .fillParentMaxWidth() .padding(horizontal = 8.dp, vertical = 0.dp), ) } } }
কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট
ListAnimatedItemsআইটেম পরিবর্তন করা হলে, অ্যানিমেটেড ট্রানজিশন সহLazyColumn-এ স্ট্রিংয়ের তালিকা দেখায়।itemsফাংশন তালিকার প্রতিটি আইটেমের জন্য একটি অনন্য কী অ্যাসাইন করে। Compose আইটেম ট্র্যাক করতে এবং সেগুলির পজিশনে হওয়া পরিবর্তন শনাক্ত করতে কী ব্যবহার করে।ListItemপ্রতিটি তালিকা আইটেমের লেআউট নির্ধারণ করে। এটি একটিheadlineContentপ্যারামিটার নেয়, যা আইটেমের মূল কন্টেন্টকে সংজ্ঞায়িত করে।animateItemমডিফায়ার আইটেম যোগ করা, সরানো ও মুভ করার ক্ষেত্রে ডিফল্ট অ্যানিমেশন প্রয়োগ করে।
নিচের স্নিপেটে এমন একটি স্ক্রিন দেখানো হয়েছে যেখানে আইটেম যোগ করা ও সরানো এবং আগে থেকে সংজ্ঞায়িত তালিকা সাজানোর কন্ট্রোল রয়েছে:
@Composable private fun ListAnimatedItemsExample( data: List<String>, modifier: Modifier = Modifier, onAddItem: () -> Unit = {}, onRemoveItem: () -> Unit = {}, resetOrder: () -> Unit = {}, onSortAlphabetically: () -> Unit = {}, onSortByLength: () -> Unit = {}, ) { val canAddItem = data.size < 10 val canRemoveItem = data.isNotEmpty() Scaffold(modifier) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) .fillMaxSize() ) { // Buttons that change the value of displayedItems. AddRemoveButtons(canAddItem, canRemoveItem, onAddItem, onRemoveItem) OrderButtons(resetOrder, onSortAlphabetically, onSortByLength) // List that displays the values of displayedItems. ListAnimatedItems(data) } } }
কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট
ListAnimatedItemsExampleএমন একটি স্ক্রিন দেখায় যেখানে আইটেম যোগ করা, সরানো ও সাজানোর কন্ট্রোল থাকে।onAddItemওonRemoveItemহল ল্যাম্বডা এক্সপ্রেশন যাAddRemoveButtons-এ পাঠানো হয় যাতে তালিকা থেকে আইটেম যোগ করা ও সরানো যায়।resetOrder,onSortAlphabeticallyওonSortByLengthহল ল্যাম্বডা এক্সপ্রেশন যাOrderButtons-এ পাস করা হয় যাতে তালিকার আইটেমের ক্রম পরিবর্তন করা যায়।
AddRemoveButtons"যোগ করুন" ও "সরিয়ে দিন" বোতাম দেখায়। এটি বোতাম চালু/বন্ধ করে এবং বোতামে ক্লিক করা ম্যানেজ করে।OrderButtonsতালিকা আবার সাজানোর বোতাম দেখায়। এটি ক্রম রিসেট করা এবং দৈর্ঘ্য বা বর্ণানুক্রমিকভাবে তালিকা সাজানোর জন্য ল্যাম্বডা ফাংশন পায়।ListAnimatedItems,ListAnimatedItemsকম্পোজ করার উপযুক্ত আইটেমকে কল করে, অ্যানিমেটেড স্ট্রিংয়ের তালিকা দেখানোর জন্যdataতালিকা পাস করে।dataঅন্য কোথাও সংজ্ঞায়িত করা হয়েছে।
এই স্নিপেট আইটেম যোগ করুন ও আইটেম মুছুন বোতাম সহ UI তৈরি করে:
@Composable private fun AddRemoveButtons( canAddItem: Boolean, canRemoveItem: Boolean, onAddItem: () -> Unit, onRemoveItem: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { Button(enabled = canAddItem, onClick = onAddItem) { Text("Add Item") } Spacer(modifier = Modifier.padding(25.dp)) Button(enabled = canRemoveItem, onClick = onRemoveItem) { Text("Delete Item") } } }
কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট
AddRemoveButtonsতালিকায় যোগ করা ও সরানো অপারেশন পারফর্ম করার জন্য বোতামের একটি সারি দেখায়।canAddItemএবংcanRemoveItemপ্যারামিটার বোতামের চালু থাকা অবস্থা কন্ট্রোল করে।canAddItemবাcanRemoveItemভুল হলে, সংশ্লিষ্ট বোতামটি বন্ধ করা থাকে।onAddItemএবংonRemoveItemপ্যারামিটার হল ল্যাম্বডা যা ব্যবহারকারী সংশ্লিষ্ট বোতামে ক্লিক করলে এক্সিকিউট হয়।
সবশেষে, এই স্নিপেটটি তালিকা সাজানোর জন্য তিনটি বোতাম দেখায় (রিসেট করুন, বর্ণানুক্রমিক এবং দৈর্ঘ্য):
@Composable private fun OrderButtons( resetOrder: () -> Unit, orderAlphabetically: () -> Unit, orderByLength: () -> Unit ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { var selectedIndex by remember { mutableIntStateOf(0) } val options = listOf("Reset", "Alphabetical", "Length") SingleChoiceSegmentedButtonRow { options.forEachIndexed { index, label -> SegmentedButton( shape = SegmentedButtonDefaults.itemShape( index = index, count = options.size ), onClick = { Log.d("AnimatedOrderedList", "selectedIndex: $selectedIndex") selectedIndex = index when (options[selectedIndex]) { "Reset" -> resetOrder() "Alphabetical" -> orderAlphabetically() "Length" -> orderByLength() } }, selected = index == selectedIndex ) { Text(label) } } } } }
কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট
OrderButtonsব্যবহারকারীদের তালিকা থেকে বাছাই করার পদ্ধতি বেছে নিতে বা তালিকার ক্রম রিসেট করতে দেওয়ার জন্যSingleChoiceSegmentedButtonRowদেখায়। একটিSegmentedButtonকম্পোনেন্ট আপনাকে বিকল্পের তালিকা থেকে একটি বিকল্প বেছে নিতে দেয়।resetOrder,orderAlphabeticallyওorderByLengthহল ল্যাম্বডা ফাংশন যেগুলি সংশ্লিষ্ট বোতাম বেছে নিলে এক্সিকিউট করা হয়।selectedIndexস্টেট ভেরিয়েবল বেছে নেওয়া বিকল্পের ট্র্যাক রাখে।
ফলাফল
আইটেমগুলি পুনর্বিন্যাস করা হলে, এই ভিডিওটি পূর্ববর্তী স্নিপেটের ফলাফল দেখায়:
স্টিকি হেডার
গ্রুপ করা ডেটার তালিকা দেখানোর সময় 'স্টিকি হেডার' প্যাটার্ন সহায়ক হয়। নিচে আপনি ‘পরিচিতির তালিকা’র একটি উদাহরণ দেখতে পাবেন, প্রতিটি পরিচিতির নামের আদ্যক্ষর অনুযায়ী গ্রুপ করা হয়েছে:

LazyColumn-এর মাধ্যমে স্টিকি হেডার পেতে, আপনি
stickyHeader()
ফাংশন, যা হেডার কন্টেন্ট প্রদান করে:
@Composable fun ListWithHeader(items: List<Item>) { LazyColumn { stickyHeader { Header() } items(items) { item -> ItemRow(item) } } }
একাধিক হেডার সহ তালিকা পেতে, যেমন উপরে দেওয়া ‘পরিচিতি তালিকা’ উদাহরণ, আপনি এগুলি করতে পারেন:
// This ideally would be done in the ViewModel val grouped = contacts.groupBy { it.firstName[0] } @Composable fun ContactsList(grouped: Map<Char, List<Contact>>) { LazyColumn { grouped.forEach { (initial, contactsForInitial) -> stickyHeader { CharacterHeader(initial) } items(contactsForInitial) { contact -> ContactListItem(contact) } } } }
স্ক্রল পজিশনে প্রতিক্রিয়া জানানো
অনেক অ্যাপকে স্ক্রল পজিশন ও আইটেম লেআউটের পরিবর্তন অনুযায়ী কাজ করতে ও শুনতে হয়।
Lazy কম্পোনেন্ট,
LazyListState হোয়েস্ট করার মাধ্যমে এই সম্ভাব্য পরিস্থিতিকে সাপোর্ট করে:
@Composable fun MessageList(messages: List<Message>) { // Remember our own LazyListState val listState = rememberLazyListState() // Provide it to LazyColumn LazyColumn(state = listState) { // ... } }
সহজ ব্যবহারের ক্ষেত্রে, অ্যাপকে সাধারণত শুধু
প্রথম দৃশ্যমান আইটেম সম্পর্কে তথ্য জানতে হয়। এর জন্য
LazyListState
firstVisibleItemIndex
এবং
firstVisibleItemScrollOffset
প্রপার্টি প্রদান করে।
ব্যবহারকারী প্রথম আইটেমটি স্ক্রল করে পেরিয়ে গেছেন কিনা তার উপর ভিত্তি করে বোতাম দেখানো ও লুকানোর উদাহরণ ব্যবহার করলে:
@Composable fun MessageList(messages: List<Message>) { Box { val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } // Show the button if the first visible item is past // the first item. We use a remembered derived state to // minimize unnecessary compositions val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } } }
কম্পোজিশনে সরাসরি স্টেট পড়া তখন কাজে লাগে যখন আপনাকে
অন্যান্য UI কম্পোজ করার উপযুক্ত আইটেম আপডেট করতে হয়, তবে এমন পরিস্থিতিও থাকে যেখানে ইভেন্টকে একই কম্পোজিশনে
হ্যান্ডেল করার প্রয়োজন হয় না। এর একটি সাধারণ উদাহরণ হল, ব্যবহারকারী স্ক্রল করে একটি নির্দিষ্ট পয়েন্ট পেরিয়ে গেলে
অ্যানালিটিক্স ইভেন্ট পাঠানো। এটি দক্ষতার সাথে
হ্যান্ডেল করতে, আমরা একটি
snapshotFlow() ব্যবহার করতে পারি:
val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .map { index -> index > 0 } .distinctUntilChanged() .filter { it } .collect { MyAnalyticsService.sendScrolledPastFirstItemEvent() } }
LazyListState বর্তমানে দেখানো হচ্ছে এমন সব আইটেম এবং স্ক্রিনে সেগুলির বাউন্ড সম্পর্কেও তথ্য প্রদান করে। এটি
layoutInfo
প্রপার্টির মাধ্যমে করা হয়। আরও তথ্যের জন্য
LazyListLayoutInfo
ক্লাস দেখুন।
স্ক্রল পজিশন কন্ট্রোল করা
স্ক্রল পজিশনে প্রতিক্রিয়া জানানোর পাশাপাশি, অ্যাপের পক্ষে স্ক্রল পজিশন
কন্ট্রোল করতে পারাটাও দরকারি।
LazyListState
scrollToItem()
ফাংশনের মাধ্যমে এটি কাজ করে, যা ‘অবিলম্বে’ স্ক্রল পজিশন
স্ন্যাপ করে এবং animateScrollToItem()
অ্যানিমেশন ব্যবহার করে স্ক্রল করে (এটি স্মুথ স্ক্রল নামেও পরিচিত):
@Composable fun MessageList(messages: List<Message>) { val listState = rememberLazyListState() // Remember a CoroutineScope to be able to launch val coroutineScope = rememberCoroutineScope() LazyColumn(state = listState) { // ... } ScrollToTopButton( onClick = { coroutineScope.launch { // Animate scroll to the first item listState.animateScrollToItem(index = 0) } } ) }
বড় ডেটা-সেট (পেজিং)
পেজিং লাইব্রেরি অ্যাপকে
প্রয়োজন অনুযায়ী আইটেমের বড় তালিকা লোড ও ডিসপ্লে করার
সুবিধা দেয়। Paging 3.0 এবং এর পরবর্তী ভার্সন
androidx.paging:paging-compose লাইব্রেরির মাধ্যমে কম্পোজ করার সুবিধা প্রদান করে।
পেজ করা কন্টেন্টের তালিকা দেখাতে, আমরা
collectAsLazyPagingItems()
এক্সটেনশন ফাংশন ব্যবহার করতে পারি এবং তারপর রিটার্ন করা
LazyPagingItems
আমাদের LazyColumn-এ items()-এ পাস করতে পারি। ভিউতে পেজিং সংক্রান্ত সহায়তার মতো, আপনি
ডেটা লোড হওয়ার সময় item null কিনা তা চেক করে প্লেসহোল্ডার দেখাতে পারেন:
@Composable fun MessageList(pager: Pager<Int, Message>) { val lazyPagingItems = pager.flow.collectAsLazyPagingItems() LazyColumn { items( lazyPagingItems.itemCount, key = lazyPagingItems.itemKey { it.id } ) { index -> val message = lazyPagingItems[index] if (message != null) { MessageRow(message) } else { MessagePlaceholder() } } } }
Lazy লেআউট ব্যবহার করা সংক্রান্ত পরামর্শ
আপনার Lazy লেআউট যাতে প্রত্যাশিতভাবে কাজ করে তা নিশ্চিত করতে, আপনি কিছু পরামর্শ বিবেচনা করতে পারেন।
০-পিক্সেল সাইজের আইটেম ব্যবহার করা এড়িয়ে চলুন
এটি এমন পরিস্থিতিতে হতে পারে যেখানে, উদাহরণস্বরূপ, আপনি পরে আপনার তালিকার আইটেম পূরণ করার জন্য ছবি ইত্যাদির মতো কিছু ডেটা অ্যাসিঙ্ক্রোনাসভাবে পাওয়ার আশা করেন। এর ফলে লেজি লেআউট তার সব আইটেম প্রথম মাপকাঠিতে কম্পোজ করবে, কারণ সেগুলির উচ্চতা ০ পিক্সেল এবং এটি সেগুলিকে ভিউপোর্টে ফিট করতে পারবে। আইটেম লোড হয়ে গেলে এবং সেগুলির উচ্চতা বেড়ে গেলে, লেজি লেআউট প্রথমবার কম্পোজ করা অন্যান্য সব আইটেম বাতিল করে দেবে কারণ সেগুলি আসলে ভিউপোর্টে ফিট করতে পারে না। এটি এড়াতে, আপনার আইটেমের ডিফল্ট সাইজিং সেট করা উচিত, যাতে লেজি লেআউট কতগুলি আইটেম আসলে ভিউপোর্টে ফিট করতে পারে তা সঠিকভাবে গণনা করতে পারে:
@Composable fun Item(imageUrl: String) { AsyncImage( model = rememberAsyncImagePainter(model = imageUrl), modifier = Modifier.size(30.dp), contentDescription = null // ... ) }
অ্যাসিঙ্ক্রোনাস লোডিংয়ের মাধ্যমে ডেটা লোড করার পরে আইটেমের আনুমানিক সাইজ সম্পর্কে জানতে পারলে, আইটেমের সাইজ লোড করার আগে ও পরে একই আছে কিনা তা নিশ্চিত করা ভালো। যেমন, কিছু প্লেসহোল্ডার যোগ করে এটি করা যেতে পারে। এর ফলে সঠিক স্ক্রল পজিশন বজায় রাখা যাবে।
একই দিকে স্ক্রল করা যায় এমন কম্পোনেন্ট নেস্ট করা এড়িয়ে চলুন
এটি শুধুমাত্র সেইসব ক্ষেত্রে প্রযোজ্য যেখানে একই দিকে স্ক্রল করা যায় এমন প্যারেন্টের মধ্যে আগে থেকে নির্ধারিত
সাইজ ছাড়া স্ক্রল করা যায় এমন চাইল্ড নেস্ট করা হয়। যেমন, উল্লম্বভাবে স্ক্রল করা যায় এমন
Column প্যারেন্ট কন্টেনারের মধ্যে নির্দিষ্ট উচ্চতা ছাড়া
LazyColumn কোনও চাইল্ড কন্টেনার নেস্ট করার চেষ্টা করা:
// throws IllegalStateException Column( modifier = Modifier.verticalScroll(state) ) { LazyColumn { // ... } }
এর পরিবর্তে, আপনার সব কম্পোজ করার মতো আইটেমকে
একটি প্যারেন্ট LazyColumn-এর মধ্যে র্যাপ করে এবং বিভিন্ন ধরনের
কন্টেন্ট পাস করার জন্য এর DSL ব্যবহার করে একই ফলাফল পাওয়া যেতে পারে। এটি একটি আইটেম এবং একাধিক তালিকা আইটেম,
সবকিছু এক জায়গায় এমিট করতে দেয়:
LazyColumn { item { Header() } items(data) { item -> PhotoItem(item) } item { Footer() } }
মনে রাখবেন, আপনি যেখানে আলাদা আলাদা দিকনির্দেশ লেআউট নেস্টিং করছেন,
যেমন, একটি স্ক্রল করা যায় এমন পেরেন্ট Row এবং একটি চাইল্ড LazyColumn, সেইসব ক্ষেত্রে অনুমতি দেওয়া হয়:
Row( modifier = Modifier.horizontalScroll(scrollState) ) { LazyColumn { // ... } }
এছাড়াও, এমন কেস যেখানে আপনি এখনও একই ডিরেকশন লেআউট ব্যবহার করেন, কিন্তু নেস্ট করা চাইল্ডের জন্য একটি নির্দিষ্ট সাইজ সেট করেন:
Column( modifier = Modifier.verticalScroll(scrollState) ) { LazyColumn( modifier = Modifier.height(200.dp) ) { // ... } }
একটি আইটেমে একাধিক এলিমেন্ট রাখা থেকে সাবধান
এই উদাহরণে, দ্বিতীয় আইটেম ল্যাম্বডা একটি ব্লকে ২টি আইটেম এমিট করে:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Item(2) } item { Item(3) } // ... }
লেজি লেআউট এটি প্রত্যাশিতভাবে হ্যান্ডেল করবে - এটি এলিমেন্টগুলিকে একটির পরে একটি করে লেআউট করবে যেন সেগুলি আলাদা আলাদা আইটেম। তবে, এটি করার ক্ষেত্রে কিছু সমস্যা রয়েছে।
একটি আইটেমের অংশ হিসেবে একাধিক এলিমেন্ট এমিট করা হলে, সেগুলিকে
একটি এন্টিটি হিসেবে ম্যানেজ করা হয়, অর্থাৎ সেগুলিকে আর আলাদা আলাদাভাবে কম্পোজ করা যায় না। স্ক্রিনে একটি
এলিমেন্ট দেখা গেলে, আইটেমের সাথে সম্পর্কিত
সব এলিমেন্ট কম্পোজ ও পরিমাপ করতে হবে। এটি অতিরিক্ত ব্যবহার করলে পারফর্ম্যান্সের উপর
খারাপ প্রভাব পড়তে পারে। সব এলিমেন্ট একটি আইটেমে রাখলে, তা
লেজি লেআউট ব্যবহারের উদ্দেশ্যকে সম্পূর্ণভাবে ব্যর্থ করে দেয়। সম্ভাব্য
পারফর্ম্যান্স সংক্রান্ত সমস্যা ছাড়াও, একটি আইটেমে আরও বেশি এলিমেন্ট যোগ করলে তা scrollToItem() ও animateScrollToItem()-এর
সাথেও ইন্টারফেয়ার করবে।
তবে, একটি আইটেমে একাধিক এলিমেন্ট রাখার বৈধ ব্যবহারিক উদাহরণ আছে, যেমন, তালিকার মধ্যে ডিভাইডার থাকা। আপনি চান না যে ডিভাইডারগুলি স্ক্রল করার ইন্ডেক্স পরিবর্তন করুক, কারণ এগুলিকে স্বতন্ত্র এলিমেন্ট হিসেবে বিবেচনা করা উচিত নয়। এছাড়াও, ডিভাইডারগুলি ছোট হওয়ায় পারফর্ম্যান্সের উপর কোনও প্রভাব পড়ে না। আগের আইটেমটি দেখা গেলে, একটি ডিভাইডারকেও সম্ভবত দেখা যেতে হবে, তাই এটি আগের আইটেমের অংশ হতে পারে:
LazyVerticalGrid( columns = GridCells.Adaptive(100.dp) ) { item { Item(0) } item { Item(1) Divider() } item { Item(2) } // ... }
কাস্টম অ্যারেঞ্জমেন্ট ব্যবহার করার কথা বিবেচনা করুন
সাধারণত, লেজি লিস্টে অনেক আইটেম থাকে এবং সেগুলি স্ক্রল করার কন্টেনারের সাইজের চেয়ে বেশি জায়গা নেয়। তবে, আপনার তালিকায় যখন অল্প কিছু আইটেম থাকে, তখন আপনার ডিজাইনে এইসব আইটেম ভিউপোর্টের মধ্যে কীভাবে পজিশন করা উচিত সেই ব্যাপারে আরও নির্দিষ্ট প্রয়োজনীয়তা থাকতে পারে।
এটি করতে, আপনি কাস্টম ভার্টিক্যাল
Arrangement
ব্যবহার করতে এবং LazyColumn-এ পাস করতে পারবেন। নিচে দেওয়া উদাহরণে, TopWithFooter
অবজেক্টকে শুধুমাত্র arrange পদ্ধতি প্রয়োগ করতে হবে। প্রথমত, এটি আইটেমগুলিকে
একটির পরে একটি করে পজিশন করবে। দ্বিতীয়ত, মোট ব্যবহৃত উচ্চতা ভিউপোর্ট উচ্চতার
চেয়ে কম হলে, এটি ফুটারকে নিচে পজিশন করবে:
object TopWithFooter : Arrangement.Vertical { override fun Density.arrange( totalSize: Int, sizes: IntArray, outPositions: IntArray ) { var y = 0 sizes.forEachIndexed { index, size -> outPositions[index] = y y += size } if (y < totalSize) { val lastIndex = outPositions.lastIndex outPositions[lastIndex] = totalSize - sizes.last() } } }
contentType যোগ করার কথা বিবেচনা করুন
Compose 1.2 থেকে শুরু করে, আপনার Lazy
লেআউটের পারফর্ম্যান্স সর্বাধিক করতে, আপনার তালিকা বা গ্রিডে
contentType
যোগ করার কথা বিবেচনা করুন। এর ফলে, আপনি যখন একাধিক ভিন্ন ধরনের আইটেম নিয়ে কোনও তালিকা বা গ্রিড
কম্পোজ করছেন, তখন লেআউটের প্রতিটি
আইটেমের জন্য কন্টেন্টের ধরন নির্দিষ্ট করতে পারবেন:
LazyColumn { items(elements, contentType = { it.type }) { // ... } }
আপনি যখন
contentType,
প্রদান করেন, তখন Compose শুধুমাত্র
একই ধরনের আইটেমের মধ্যে কম্পোজিশন আবার ব্যবহার করতে পারে। আপনি যখন
একই ধরনের স্ট্রাকচার সহ আইটেম কম্পোজ করেন, তখন তা আবার ব্যবহার করা আরও বেশি কার্যকর হয়। কন্টেন্টের ধরন প্রদান করলে তা নিশ্চিত করে যে
কম্পোজ, সম্পূর্ণ
আলাদা ধরন B-এর আইটেমের উপরে ধরন A-এর আইটেম কম্পোজ করার চেষ্টা করে না। এটি কম্পোজিশন
পুনর্ব্যবহার করা এবং আপনার লেজি লেআউটের পারফর্ম্যান্সের সুবিধা সর্বাধিক করতে সাহায্য করে।
পারফর্ম্যান্স পরিমাপ করা
আপনি রিলিজ মোডে এবং R8 অপ্টিমাইজেশন চালু করে চালালে তবেই কোনও লেজি লেআউটের পারফর্ম্যান্স নির্ভরযোগ্যভাবে পরিমাপ করতে পারবেন। ডিবাগ বিল্ডে, লেজি লেআউট স্ক্রলিং আরও ধীরে ধীরে দেখা যেতে পারে। এই বিষয়ে আরও জানতে, কম্পোজ পারফর্ম্যান্স পড়ুন।
অতিরিক্ত রিসোর্স
- একটি সসীম স্ক্রল করা যায় এমন তালিকা তৈরি করা
- স্ক্রল করা যায় এমন গ্রিড তৈরি করুন
- তালিকায় নেস্ট করা স্ক্রল করা যায় এমন আইটেম দেখানো
- টাইপ করার সময় তালিকা ফিল্টার করা
- তালিকা ও পেজিংয়ের মাধ্যমে ডেটা লেজি লোড করা
- একাধিক ধরনের আইটেম ব্যবহার করে তালিকা তৈরি করা
- ভিডিও: কম্পোজে তালিকা
আপনার জন্য সাজেস্ট করা হয়েছে
- মনে রাখবেন: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট দেখানো হয়
- Lazy list-এ
RecyclerViewমাইগ্রেট করুন - Compose-এ UI স্টেট সেভ করা
- Jetpack Compose-এর জন্য Kotlin