তালিকা এবং গ্রিড

অনেক অ্যাপ্লিকেশান আইটেম সংগ্রহ প্রদর্শন করতে হবে. এই নথিটি ব্যাখ্যা করে কিভাবে আপনি Jetpack রচনায় এটি দক্ষতার সাথে করতে পারেন।

আপনি যদি জানেন যে আপনার ব্যবহারের ক্ষেত্রে কোনো স্ক্রোলিং প্রয়োজন নেই, তাহলে আপনি একটি সাধারণ Column বা Row (দিকনির্দেশের উপর নির্ভর করে) ব্যবহার করতে চাইতে পারেন এবং নিম্নলিখিত উপায়ে একটি তালিকার উপর পুনরাবৃত্তি করে প্রতিটি আইটেমের বিষয়বস্তু নির্গত করতে পারেন:

@Composable
fun MessageList(messages: List<Message>) {
    Column {
        messages.forEach { message ->
            MessageRow(message)
        }
    }
}

আমরা verticalScroll() মডিফায়ার ব্যবহার করে Column স্ক্রোলযোগ্য করে তুলতে পারি।

অলস তালিকা

আপনি যদি প্রচুর পরিমাণে আইটেম (বা অজানা দৈর্ঘ্যের একটি তালিকা) প্রদর্শন করতে চান, তাহলে Column মতো একটি লেআউট ব্যবহার করলে কার্যক্ষমতার সমস্যা হতে পারে, যেহেতু সমস্ত আইটেমগুলি দৃশ্যমান হোক বা না হোক সেগুলি তৈরি করা হবে এবং সাজানো হবে।

কম্পোজ উপাদানগুলির একটি সেট সরবরাহ করে যা শুধুমাত্র উপাদানগুলির ভিউপোর্টে দৃশ্যমান আইটেমগুলি রচনা এবং লেআউট করে৷ এই উপাদানগুলির মধ্যে রয়েছে LazyColumn এবং LazyRow

নাম থেকে বোঝা যায়, LazyColumn এবং LazyRow মধ্যে পার্থক্য হল স্থিতিবিন্যাস যেখানে তারা তাদের আইটেমগুলিকে স্ক্রোল করে। LazyColumn একটি উল্লম্বভাবে স্ক্রলিং তালিকা তৈরি করে এবং LazyRow একটি অনুভূমিকভাবে স্ক্রলিং তালিকা তৈরি করে।

অলস উপাদানগুলি রচনার বেশিরভাগ লেআউটের থেকে আলাদা৷ একটি @Composable কন্টেন্ট ব্লক প্যারামিটার গ্রহণ করার পরিবর্তে, অ্যাপগুলিকে সরাসরি কম্পোজেবল নির্গত করার অনুমতি দেয়, অলস উপাদানগুলি একটি LazyListScope.() ব্লক প্রদান করে। এই LazyListScope ব্লকটি একটি DSL অফার করে যা অ্যাপগুলিকে আইটেমের বিষয়বস্তু বর্ণনা করতে দেয়। অলস কম্পোনেন্ট তারপর লেআউট এবং স্ক্রোল অবস্থান অনুযায়ী প্রতিটি আইটেমের বিষয়বস্তু যোগ করার জন্য দায়ী।

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.() ব্যবহার করে৷

একটি ফোনের স্ক্রিনশট ফটোগুলির একটি গ্রিড দেখাচ্ছে৷

LazyVerticalGridcolumns প্যারামিটার এবং LazyHorizontalGridrows প্যারামিটার নিয়ন্ত্রণ করে যে কীভাবে কলাম বা সারিগুলিতে কোষ গঠিত হয়। নিম্নলিখিত উদাহরণটি একটি গ্রিডে আইটেমগুলি প্রদর্শন করে, প্রতিটি কলামকে কমপক্ষে 128.dp প্রশস্ত করতে GridCells.Adaptive ব্যবহার করে:

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 128.dp)
) {
    items(photos) { photo ->
        PhotoItem(photo)
    }
}

LazyVerticalGrid আপনাকে আইটেমগুলির জন্য একটি প্রস্থ নির্দিষ্ট করতে দেয় এবং তারপরে গ্রিড যতটা সম্ভব কলাম ফিট করবে। কলামের সংখ্যা গণনা করার পরে যেকোন অবশিষ্ট প্রস্থ কলামগুলির মধ্যে সমানভাবে বিতরণ করা হয়। সাইজিংয়ের এই অভিযোজিত উপায়টি বিভিন্ন স্ক্রিনের আকার জুড়ে আইটেমগুলির সেট প্রদর্শনের জন্য বিশেষভাবে কার্যকর।

আপনি যদি জানেন যে কলামের সঠিক সংখ্যা ব্যবহার করা হবে, তাহলে আপনি এর পরিবর্তে GridCells.Fixed একটি উদাহরণ প্রদান করতে পারেন। প্রয়োজনীয় কলামের সংখ্যা সহ স্থির।

যদি আপনার ডিজাইনের জন্য শুধুমাত্র নির্দিষ্ট আইটেমের অ-মানক মাত্রার প্রয়োজন হয়, তাহলে আপনি আইটেমগুলির জন্য কাস্টম কলাম স্প্যান প্রদানের জন্য গ্রিড সমর্থন ব্যবহার করতে পারেন। LazyGridScope DSL item এবং items পদ্ধতির span প্যারামিটার সহ কলাম স্প্যানটি নির্দিষ্ট করুন। maxLineSpan , স্প্যান স্কোপের মানগুলির মধ্যে একটি, বিশেষ করে উপযোগী যখন আপনি অভিযোজিত আকার ব্যবহার করছেন, কারণ কলামের সংখ্যা স্থির নয়। এই উদাহরণটি দেখায় কিভাবে একটি সম্পূর্ণ সারি স্প্যান প্রদান করতে হয়:

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 30.dp)
) {
    item(span = {
        // LazyGridItemSpanScope:
        // maxLineSpan
        GridItemSpan(maxLineSpan)
    }) {
        CategoryCard("Fruits")
    }
    // ...
}

অলস staggered গ্রিড

LazyVerticalStaggeredGrid এবং LazyHorizontalStaggeredGrid হল কম্পোজেবল যা আপনাকে আইটেমগুলির একটি অলস-লোডেড, স্তব্ধ গ্রিড তৈরি করতে দেয়। একটি অলস উল্লম্ব স্তব্ধ গ্রিড তার আইটেমগুলিকে একটি উল্লম্বভাবে স্ক্রোলযোগ্য পাত্রে প্রদর্শন করে যা একাধিক কলাম জুড়ে বিস্তৃত এবং পৃথক আইটেমগুলিকে বিভিন্ন উচ্চতা হতে দেয়৷ অলস অনুভূমিক গ্রিডগুলি বিভিন্ন প্রস্থের আইটেমগুলির সাথে অনুভূমিক অক্ষে একই আচরণ করে।

নিম্নলিখিত স্নিপেটটি প্রতি আইটেম 200.dp প্রস্থ সহ LazyVerticalStaggeredGrid ব্যবহার করার একটি মৌলিক উদাহরণ:

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()
)

চিত্র 1. অলস স্তব্ধ উল্লম্ব গ্রিডের উদাহরণ

একটি নির্দিষ্ট সংখ্যক কলাম সেট করতে, আপনি 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 staggered grid of images in Compose
চিত্র 2. স্থির কলাম সহ অলস স্তব্ধ উল্লম্ব গ্রিডের উদাহরণ

বিষয়বস্তু প্যাডিং

কখনও কখনও আপনাকে সামগ্রীর প্রান্তগুলির চারপাশে প্যাডিং যুক্ত করতে হবে৷ অলস উপাদানগুলি আপনাকে এটি সমর্থন করার জন্য contentPadding প্যারামিটারে কিছু PaddingValues পাস করার অনুমতি দেয়:

LazyColumn(
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
) {
    // ...
}

এই উদাহরণে, আমরা অনুভূমিক প্রান্তে 16.dp প্যাডিং যোগ করি (বাম এবং ডান), এবং তারপর বিষয়বস্তুর উপরে এবং নীচে 8.dp

অনুগ্রহ করে মনে রাখবেন যে এই প্যাডিংটি সামগ্রীতে প্রয়োগ করা হয়েছে, LazyColumn এ নয়। উপরের উদাহরণে, প্রথম আইটেমটি তার উপরে 8.dp প্যাডিং যোগ করবে, শেষ আইটেমটি তার নীচে 8.dp যোগ করবে এবং সমস্ত আইটেমের বাম এবং ডানদিকে 16.dp প্যাডিং থাকবে।

বিষয়বস্তুর ব্যবধান

আইটেমগুলির মধ্যে ব্যবধান যোগ করতে, আপনি 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)
    }
}

আইটেম কী

ডিফল্টরূপে, প্রতিটি আইটেমের অবস্থা তালিকা বা গ্রিডে আইটেমের অবস্থানের বিপরীতে কী করা হয়। যাইহোক, ডেটা সেট পরিবর্তিত হলে এটি সমস্যার কারণ হতে পারে, যেহেতু যে আইটেমগুলি অবস্থান পরিবর্তন করে তা কার্যকরভাবে কোনো মনে রাখা অবস্থা হারায়। আপনি যদি LazyColumn এর মধ্যে LazyRow এর দৃশ্যকল্প কল্পনা করেন, যদি সারিটি আইটেমের অবস্থান পরিবর্তন করে, তাহলে ব্যবহারকারী সারির মধ্যে তাদের স্ক্রোল অবস্থান হারাবেন।

এটি মোকাবেলা করার জন্য, আপনি প্রতিটি আইটেমের জন্য একটি স্থিতিশীল এবং অনন্য কী প্রদান করতে পারেন, key প্যারামিটারে একটি ব্লক প্রদান করে। একটি স্থিতিশীল কী প্রদান করা আইটেম অবস্থাকে ডেটা-সেট পরিবর্তন জুড়ে সামঞ্জস্যপূর্ণ হতে সক্ষম করে:

LazyColumn {
    items(
        items = messages,
        key = { message ->
            // Return a stable + unique key for the item
            message.id
        }
    ) { message ->
        MessageRow(message)
    }
}

কী প্রদান করার মাধ্যমে, আপনি সঠিকভাবে পুনর্বিন্যাস পরিচালনা করতে রচনা করতে সহায়তা করেন। উদাহরণ স্বরূপ, যদি আপনার আইটেমটিতে মনে রাখা অবস্থা থাকে, তাহলে সেটির অবস্থান পরিবর্তন হলে, সেটিং কী কম্পোজকে আইটেমের সাথে এই অবস্থাটিকে একত্রে সরানোর অনুমতি দেবে।

LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = remember {
            Random.nextInt()
        }
    }
}

যাইহোক, আইটেম কী হিসাবে আপনি কী ধরণের ব্যবহার করতে পারেন তার একটি সীমাবদ্ধতা রয়েছে। কী এর ধরনটি অবশ্যই Bundle দ্বারা সমর্থিত হতে হবে, অ্যাক্টিভিটি পুনরায় তৈরি করার সময় স্টেট রাখার জন্য Android এর মেকানিজম। Bundle আদিম, enums বা পার্সেলেবলের মত ধরনের সমর্থন করে।

LazyColumn {
    items(books, key = {
        // primitives, enums, Parcelable, etc.
    }) {
        // ...
    }
}

কীটি অবশ্যই Bundle দ্বারা সমর্থিত হতে হবে যাতে কম্পোজেবল আইটেমটির ভিতরে rememberSaveable পুনরুদ্ধার করা যায় যখন অ্যাক্টিভিটি পুনরায় তৈরি করা হয়, এমনকি আপনি যখন এই আইটেমটি থেকে দূরে স্ক্রোল করেন এবং পিছনে স্ক্রোল করেন।

LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = rememberSaveable {
            Random.nextInt()
        }
    }
}

আইটেম অ্যানিমেশন

আপনি যদি RecyclerView উইজেট ব্যবহার করে থাকেন, তাহলে আপনি জানতে পারবেন যে এটি স্বয়ংক্রিয়ভাবে আইটেম পরিবর্তনগুলিকে অ্যানিমেট করে । অলস বিন্যাসগুলি আইটেম পুনরায় সাজানোর জন্য একই কার্যকারিতা প্রদান করে। এপিআই সহজ - আপনাকে কেবল আইটেম সামগ্রীতে animateItemPlacement সংশোধক সেট করতে হবে:

LazyColumn {
    items(books, key = { it.id }) {
        Row(Modifier.animateItemPlacement()) {
            // ...
        }
    }
}

আপনি এমনকি কাস্টম অ্যানিমেশন স্পেসিফিকেশন প্রদান করতে পারেন, যদি আপনার প্রয়োজন হয়:

LazyColumn {
    items(books, key = { it.id }) {
        Row(
            Modifier.animateItemPlacement(
                tween(durationMillis = 250)
            )
        ) {
            // ...
        }
    }
}

নিশ্চিত করুন যে আপনি আপনার আইটেমগুলির জন্য কীগুলি প্রদান করেছেন যাতে সরানো উপাদানটির জন্য নতুন অবস্থান খুঁজে পাওয়া সম্ভব হয়৷

পুনর্বিন্যাস ছাড়াও, সংযোজন এবং অপসারণের জন্য আইটেম অ্যানিমেশনগুলি বর্তমানে বিকাশাধীন। আপনি 150812265 ইস্যুতে অগ্রগতি ট্র্যাক করতে পারেন।

স্টিকি হেডার (পরীক্ষামূলক)

দলবদ্ধ ডেটার তালিকা প্রদর্শন করার সময় 'স্টিকি হেডার' প্যাটার্ন সহায়ক। নীচে আপনি একটি 'পরিচিতি তালিকা' এর একটি উদাহরণ দেখতে পারেন, প্রতিটি পরিচিতির প্রাথমিক দ্বারা গোষ্ঠীবদ্ধ:

একটি পরিচিতি তালিকার মাধ্যমে উপরে এবং নীচে স্ক্রোল করা একটি ফোনের ভিডিও৷

LazyColumn এর সাথে একটি স্টিকি হেডার অর্জন করতে, আপনি পরীক্ষামূলক stickyHeader() ফাংশন ব্যবহার করতে পারেন, শিরোনাম সামগ্রী প্রদান করে:

@OptIn(ExperimentalFoundationApi::class)
@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] }

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ContactsList(grouped: Map<Char, List<Contact>>) {
    LazyColumn {
        grouped.forEach { (initial, contactsForInitial) ->
            stickyHeader {
                CharacterHeader(initial)
            }

            items(contactsForInitial) { contact ->
                ContactListItem(contact)
            }
        }
    }
}

স্ক্রল অবস্থানে প্রতিক্রিয়া

অনেক অ্যাপের স্ক্রোল পজিশন এবং আইটেম লেআউট পরিবর্তনের জন্য প্রতিক্রিয়া জানাতে এবং শুনতে হয়। অলস উপাদানগুলি LazyListState উত্তোলন করে এই ব্যবহারের ক্ষেত্রে সমর্থন করে:

@Composable
fun MessageList(messages: List<Message>) {
    // Remember our own LazyListState
    val listState = rememberLazyListState()

    // Provide it to LazyColumn
    LazyColumn(state = listState) {
        // ...
    }
}

সাধারণ ব্যবহারের ক্ষেত্রে, অ্যাপগুলিকে সাধারণত শুধুমাত্র প্রথম দৃশ্যমান আইটেম সম্পর্কে তথ্য জানতে হবে। এই LazyListState এর জন্য firstVisibleItemIndex এবং firstVisibleItemScrollOffset বৈশিষ্ট্য প্রদান করে।

ব্যবহারকারী প্রথম আইটেমটি স্ক্রোল করেছে কিনা তার উপর ভিত্তি করে যদি আমরা একটি বোতাম দেখানো এবং লুকানোর উদাহরণ ব্যবহার করি:

@OptIn(ExperimentalAnimationApi::class)
@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)
            }
        }
    )
}

বড় ডেটা-সেট (পেজিং)

পেজিং লাইব্রেরি অ্যাপগুলিকে আইটেমের বড় তালিকা সমর্থন করতে সক্ষম করে, প্রয়োজনে তালিকার ছোট অংশগুলি লোড করা এবং প্রদর্শন করা। পেজিং 3.0 এবং পরবর্তীতে androidx.paging:paging-compose লাইব্রেরির মাধ্যমে কম্পোজ সমর্থন প্রদান করে।

পৃষ্ঠাযুক্ত সামগ্রীর একটি তালিকা প্রদর্শন করতে, আমরা collectAsLazyPagingItems() এক্সটেনশন ফাংশন ব্যবহার করতে পারি এবং তারপরে আমাদের LazyColumnitems() এ ফেরত LazyPagingItems এ পাস করতে পারি। ভিউতে পেজিং সমর্থনের মতো, আপনি 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()
            }
        }
    }
}

অলস লেআউট ব্যবহার করার টিপস

আপনার অলস লেআউটগুলি উদ্দেশ্য অনুযায়ী কাজ করছে তা নিশ্চিত করতে আপনি কয়েকটি টিপস বিবেচনা করতে পারেন।

0-পিক্সেল আকারের আইটেম ব্যবহার করা এড়িয়ে চলুন

এটি এমন পরিস্থিতিতে ঘটতে পারে যেখানে, উদাহরণস্বরূপ, আপনি পরবর্তী পর্যায়ে আপনার তালিকার আইটেমগুলি পূরণ করতে ইমেজের মতো কিছু ডেটা অ্যাসিঙ্ক্রোনাসভাবে পুনরুদ্ধার করার আশা করেন। এটি অলস লেআউটটিকে প্রথম পরিমাপে তার সমস্ত আইটেম রচনা করতে কারণ হবে, কারণ তাদের উচ্চতা 0 পিক্সেল এবং এটি সেগুলিকে ভিউপোর্টে ফিট করতে পারে৷ একবার আইটেমগুলি লোড হয়ে গেলে এবং তাদের উচ্চতা প্রসারিত হয়ে গেলে, অলস লেআউটগুলি অন্য সমস্ত আইটেমগুলিকে বাতিল করে দেবে যেগুলি অপ্রয়োজনীয়ভাবে প্রথমবার তৈরি করা হয়েছে কারণ তারা আসলে ভিউপোর্টের সাথে মানানসই নয়৷ এটি এড়াতে, আপনার আইটেমগুলিতে ডিফল্ট আকার নির্ধারণ করা উচিত, যাতে অলস বিন্যাসটি ভিউপোর্টে আসলে কতগুলি আইটেম ফিট করতে পারে তার সঠিক গণনা করতে পারে:

@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)
    ) {
        // ...
    }
}

একটি আইটেম একাধিক উপাদান নির্বাণ সতর্ক থাকুন

এই উদাহরণে, দ্বিতীয় আইটেম ল্যাম্বডা একটি ব্লকে 2টি আইটেম নির্গত করে:

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 যোগ করার কথা বিবেচনা করুন

কম্পোজ 1.2 দিয়ে শুরু করে, আপনার অলস লেআউটের কার্যকারিতা সর্বাধিক করার জন্য, আপনার তালিকা বা গ্রিডগুলিতে contentType যোগ করার কথা বিবেচনা করুন। এটি আপনাকে লেআউটের প্রতিটি আইটেমের জন্য বিষয়বস্তুর প্রকার নির্দিষ্ট করতে দেয়, এমন ক্ষেত্রে যেখানে আপনি একটি তালিকা বা একাধিক বিভিন্ন ধরনের আইটেম সমন্বিত একটি গ্রিড রচনা করছেন:

LazyColumn {
    items(elements, contentType = { it.type }) {
        // ...
    }
}

আপনি যখন contentType প্রদান করেন, রচনা শুধুমাত্র একই ধরনের আইটেমগুলির মধ্যে রচনাগুলি পুনরায় ব্যবহার করতে সক্ষম হয়৷ আপনি যখন একই ধরনের কাঠামোর আইটেমগুলি রচনা করেন তখন পুনঃব্যবহার করা আরও কার্যকরী হয়, তাই বিষয়বস্তুর প্রকারগুলি প্রদান করা নিশ্চিত করে যে রচনা B টাইপের সম্পূর্ণ ভিন্ন আইটেমের উপরে টাইপ A-এর একটি আইটেম রচনা করার চেষ্টা করে না। এটি কম্পোজিশন পুনঃব্যবহারের সুবিধাগুলি সর্বাধিক করতে সাহায্য করে এবং আপনার অলস বিন্যাস কর্মক্ষমতা.

পরিমাপ কর্মক্ষমতা

রিলিজ মোডে চলাকালীন এবং R8 অপ্টিমাইজেশান সক্ষম থাকা অবস্থায় আপনি শুধুমাত্র একটি অলস লেআউটের কার্যকারিতা নির্ভরযোগ্যভাবে পরিমাপ করতে পারেন। ডিবাগ বিল্ডগুলিতে, অলস লেআউট স্ক্রোলিং ধীরে ধীরে প্রদর্শিত হতে পারে। এই বিষয়ে আরও তথ্যের জন্য, রচনা সম্পাদনার মাধ্যমে পড়ুন।

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}