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

অনেক অ্যাপ্লিকেশান আইটেম সংগ্রহ প্রদর্শন করতে হবে. এই নথিটি ব্যাখ্যা করে কিভাবে আপনি 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 {
    // 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)
            )
        ) {
            // ...
        }
    }
}

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

পুনর্বিন্যাস ছাড়াও, সংযোজন এবং অপসারণের জন্য আইটেম অ্যানিমেশনগুলি বর্তমানে বিকাশাধীন। আপনি 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 বৈশিষ্ট্য প্রদান করে।

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

@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 %} ,

অনেক অ্যাপ্লিকেশান আইটেম সংগ্রহ প্রদর্শন করতে হবে. এই নথিটি ব্যাখ্যা করে কিভাবে আপনি 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 {
    // 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)
            )
        ) {
            // ...
        }
    }
}

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

পুনর্বিন্যাস ছাড়াও, সংযোজন এবং অপসারণের জন্য আইটেম অ্যানিমেশনগুলি বর্তমানে বিকাশাধীন। আপনি 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 বৈশিষ্ট্য প্রদান করে।

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

@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 সরবরাহ করেন, রচনাটি কেবল একই ধরণের আইটেমগুলির মধ্যে রচনাগুলি পুনরায় ব্যবহার করতে সক্ষম হয়। আপনি যখন অনুরূপ কাঠামোর আইটেমগুলি রচনা করেন তখন পুনরায় ব্যবহার করা আরও দক্ষ হিসাবে, সামগ্রীর প্রকারগুলি সরবরাহ করা নিশ্চিত করে যে প্রকার বি এর সম্পূর্ণ ভিন্ন আইটেমের শীর্ষে টাইপ এ এর ​​কোনও আইটেম রচনা করার চেষ্টা করে না এটি রচনাটি পুনরায় ব্যবহার করার সুবিধাগুলি সর্বাধিক করতে সহায়তা করে এবং আপনার অলস লেআউট পারফরম্যান্স।

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

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

{ % ভারব্যাটিম %} { % endverbatim %} { % ভারব্যাটিম %} { % endverbatim %},

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

আপনি যদি জানেন যে আপনার ব্যবহারের ক্ষেত্রে কোনও স্ক্রোলিংয়ের প্রয়োজন নেই, আপনি একটি সাধারণ 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 ব্লকটি একটি ডিএসএল সরবরাহ করে যা অ্যাপ্লিকেশনগুলিকে আইটেমের সামগ্রীগুলি বর্ণনা করতে দেয়। অলস উপাদানটি তখন লেআউট এবং স্ক্রোল অবস্থানের দ্বারা প্রয়োজনীয় প্রতিটি আইটেমের সামগ্রী যুক্ত করার জন্য দায়বদ্ধ।

LazyListScope ডিএসএল

LazyListScope ডিএসএল বিন্যাসে আইটেমগুলি বর্ণনা করার জন্য বেশ কয়েকটি ফাংশন সরবরাহ করে। সর্বাধিক বেসিক, 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 কমপোজেবলগুলি একটি গ্রিডে আইটেমগুলি প্রদর্শনের জন্য সহায়তা সরবরাহ করে। একটি অলস উল্লম্ব গ্রিডটি একাধিক কলাম জুড়ে বিস্তৃত একটি উল্লম্ব স্ক্রোলযোগ্য পাত্রে তার আইটেমগুলি প্রদর্শন করবে, যখন অলস অনুভূমিক গ্রিডগুলি অনুভূমিক অক্ষগুলিতে একই আচরণ করবে।

গ্রিডগুলির তালিকার মতো একই শক্তিশালী এপিআই ক্ষমতা রয়েছে এবং তারা সামগ্রীটি বর্ণনা করার জন্য একটি খুব অনুরূপ ডিএসএল - LazyGridScope.()

একটি ফোনের স্ক্রিনশট ফটোগুলির একটি গ্রিড দেখায়

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

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

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

আপনি যদি কলামগুলির সঠিক সংখ্যাটি ব্যবহার করতে জানেন তবে আপনি পরিবর্তে GridCells.Fixed একটি উদাহরণ সরবরাহ করতে পারেন F

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

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

অলস স্তম্ভিত গ্রিড

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.Fixed(columns) StaggeredGridCells.Adaptive পরিবর্তে ব্যবহার করতে পারেন। এটি কলামগুলির সংখ্যা (বা একটি অনুভূমিক গ্রিডের জন্য সারি) দ্বারা উপলভ্য প্রস্থকে বিভক্ত করে এবং প্রতিটি আইটেম সেই প্রস্থটি গ্রহণ করে (বা একটি অনুভূমিক গ্রিডের জন্য উচ্চতা):

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 দ্বারা কীটির ধরণটি অবশ্যই সমর্থন করতে হবে। Bundle আদিম, এনাম বা পার্সেলেবলের মতো প্রকারগুলিকে সমর্থন করে।

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

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

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

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

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

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

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

পুনরায় অর্ডারগুলি বাদ দিয়ে, সংযোজন এবং অপসারণের জন্য আইটেম অ্যানিমেশনগুলি বর্তমানে বিকাশে রয়েছে। আপনি 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 বৈশিষ্ট্য সরবরাহ করে।

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

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

আপনার অন্যান্য ইউআই কমপোজেবলগুলি আপডেট করার দরকার হলে সরাসরি রচনাটিতে রাজ্যটি পড়া কার্যকর, তবে এমন কিছু পরিস্থিতিও রয়েছে যেখানে ইভেন্টটি একই রচনায় পরিচালনা করার দরকার নেই। এর একটি সাধারণ উদাহরণ ব্যবহারকারী একটি নির্দিষ্ট পয়েন্ট পেরিয়ে যাওয়ার পরে একটি বিশ্লেষণ ইভেন্ট প্রেরণ করা। এটি দক্ষতার সাথে পরিচালনা করতে, আমরা একটি 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() এক্সটেনশন ফাংশনটি ব্যবহার করতে পারি এবং তারপরে আমাদের LazyColumn items() ফিরে আসা 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 ভিতরে মোড়ানো এবং বিভিন্ন ধরণের সামগ্রীতে পাস করার জন্য এর ডিএসএল ব্যবহার করে অর্জন করা যেতে পারে। এটি একক আইটেম, পাশাপাশি একাধিক তালিকা আইটেমগুলি নির্গত করতে সক্ষম করে, সমস্ত এক জায়গায়:

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 সরবরাহ করেন, রচনাটি কেবল একই ধরণের আইটেমগুলির মধ্যে রচনাগুলি পুনরায় ব্যবহার করতে সক্ষম হয়। আপনি যখন অনুরূপ কাঠামোর আইটেমগুলি রচনা করেন তখন পুনরায় ব্যবহার করা আরও দক্ষ হিসাবে, সামগ্রীর প্রকারগুলি সরবরাহ করা নিশ্চিত করে যে প্রকার বি এর সম্পূর্ণ ভিন্ন আইটেমের শীর্ষে টাইপ এ এর ​​কোনও আইটেম রচনা করার চেষ্টা করে না এটি রচনাটি পুনরায় ব্যবহার করার সুবিধাগুলি সর্বাধিক করতে সহায়তা করে এবং আপনার অলস লেআউট পারফরম্যান্স।

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

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

{ % ভারব্যাটিম %} { % endverbatim %} { % ভারব্যাটিম %} { % endverbatim %},

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

আপনি যদি জানেন যে আপনার ব্যবহারের ক্ষেত্রে কোনও স্ক্রোলিংয়ের প্রয়োজন নেই, আপনি একটি সাধারণ 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 ব্লকটি একটি ডিএসএল সরবরাহ করে যা অ্যাপ্লিকেশনগুলিকে আইটেমের সামগ্রীগুলি বর্ণনা করতে দেয়। অলস উপাদানটি তখন লেআউট এবং স্ক্রোল অবস্থানের দ্বারা প্রয়োজনীয় প্রতিটি আইটেমের সামগ্রী যুক্ত করার জন্য দায়বদ্ধ।

LazyListScope ডিএসএল

LazyListScope ডিএসএল বিন্যাসে আইটেমগুলি বর্ণনা করার জন্য বেশ কয়েকটি ফাংশন সরবরাহ করে। সর্বাধিক বেসিক, 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 কমপোজেবলগুলি একটি গ্রিডে আইটেমগুলি প্রদর্শনের জন্য সহায়তা সরবরাহ করে। একটি অলস উল্লম্ব গ্রিডটি একাধিক কলাম জুড়ে বিস্তৃত একটি উল্লম্ব স্ক্রোলযোগ্য পাত্রে তার আইটেমগুলি প্রদর্শন করবে, যখন অলস অনুভূমিক গ্রিডগুলি অনুভূমিক অক্ষগুলিতে একই আচরণ করবে।

গ্রিডগুলির তালিকার মতো একই শক্তিশালী এপিআই ক্ষমতা রয়েছে এবং তারা সামগ্রীটি বর্ণনা করার জন্য একটি খুব অনুরূপ ডিএসএল - LazyGridScope.()

একটি ফোনের স্ক্রিনশট ফটোগুলির একটি গ্রিড দেখায়

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

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

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

আপনি যদি কলামগুলির সঠিক সংখ্যাটি ব্যবহার করতে জানেন তবে আপনি পরিবর্তে GridCells.Fixed একটি উদাহরণ সরবরাহ করতে পারেন F

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

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

অলস স্তম্ভিত গ্রিড

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.Fixed(columns) StaggeredGridCells.Adaptive পরিবর্তে ব্যবহার করতে পারেন। এটি কলামগুলির সংখ্যা (বা একটি অনুভূমিক গ্রিডের জন্য সারি) দ্বারা উপলভ্য প্রস্থকে বিভক্ত করে এবং প্রতিটি আইটেম সেই প্রস্থটি গ্রহণ করে (বা একটি অনুভূমিক গ্রিডের জন্য উচ্চতা):

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 দ্বারা কীটির ধরণটি অবশ্যই সমর্থন করতে হবে। Bundle আদিম, এনাম বা পার্সেলেবলের মতো প্রকারগুলিকে সমর্থন করে।

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

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

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

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

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

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

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

পুনরায় অর্ডারগুলি বাদ দিয়ে, সংযোজন এবং অপসারণের জন্য আইটেম অ্যানিমেশনগুলি বর্তমানে বিকাশে রয়েছে। আপনি 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 বৈশিষ্ট্য সরবরাহ করে।

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

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

আপনার অন্যান্য ইউআই কমপোজেবলগুলি আপডেট করার দরকার হলে সরাসরি রচনাটিতে রাজ্যটি পড়া কার্যকর, তবে এমন কিছু পরিস্থিতিও রয়েছে যেখানে ইভেন্টটি একই রচনায় পরিচালনা করার দরকার নেই। এর একটি সাধারণ উদাহরণ ব্যবহারকারী একটি নির্দিষ্ট পয়েন্ট পেরিয়ে যাওয়ার পরে একটি বিশ্লেষণ ইভেন্ট প্রেরণ করা। এটি দক্ষতার সাথে পরিচালনা করতে, আমরা একটি 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() এক্সটেনশন ফাংশনটি ব্যবহার করতে পারি এবং তারপরে আমাদের LazyColumn items() ফিরে আসা 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 {
        // ...
    }
}

Instead, the same result can be achieved by wrapping all of your composables inside one parent LazyColumn and using its DSL to pass in different types of content. This enables emitting single items, as well as multiple list items, all in one place:

LazyColumn {
    item {
        Header()
    }
    items(data) { item ->
        PhotoItem(item)
    }
    item {
        Footer()
    }
}

Keep in mind that cases where you're nesting different direction layouts, for example, a scrollable parent Row and a child LazyColumn , are allowed:

Row(
    modifier = Modifier.horizontalScroll(scrollState)
) {
    LazyColumn {
        // ...
    }
}

As well as cases where you still use the same direction layouts, but also set a fixed size to the nested children:

Column(
    modifier = Modifier.verticalScroll(scrollState)
) {
    LazyColumn(
        modifier = Modifier.height(200.dp)
    ) {
        // ...
    }
}

Beware of putting multiple elements in one item

In this example, the second item lambda emits 2 items in one block:

LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Item(2)
    }
    item { Item(3) }
    // ...
}

Lazy layouts will handle this as expected - they will lay out elements one after another as if they were different items. However, there are a couple of problems with doing so.

When multiple elements are emitted as part of one item, they are handled as one entity, meaning that they cannot be composed individually anymore. If one element becomes visible on the screen, then all elements corresponding to the item have to be composed and measured. This can hurt performance if used excessively. In the extreme case of putting all elements in one item, it completely defeats the purpose of using Lazy layouts. Apart from potential performance issues, putting more elements in one item will also interfere with scrollToItem() & animateScrollToItem() .

However, there are valid use cases for putting multiple elements in one item, like having dividers inside a list. You do not want dividers to change scrolling indices, as they shouldn't be considered independent elements. Also, performance will not be affected as dividers are small. A divider will likely need to be visible when the item before it is visible, so they can be part of the previous item:

LazyVerticalGrid(
    columns = GridCells.Adaptive(100.dp)
) {
    item { Item(0) }
    item {
        Item(1)
        Divider()
    }
    item { Item(2) }
    // ...
}

Consider using custom arrangements

Usually Lazy lists have many items, and they occupy more than the size of the scrolling container. However, when your list is populated with few items, your design can have more specific requirements for how these should be positioned in the viewport.

To achieve this, you can use custom vertical Arrangement and pass it to the LazyColumn . In the following example, the TopWithFooter object only needs to implement the arrange method. Firstly, it will position items one after another. Secondly, if the total used height is lower than the viewport height, it will position the footer at the bottom:

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

Consider adding contentType

Starting with Compose 1.2, in order to maximize the performance of your Lazy layout, consider adding contentType to your lists or grids. This allows you to specify the content type for each item of the layout, in cases where you're composing a list or a grid consisting of multiple different types of items:

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

When you provide the contentType , Compose is able to reuse compositions only between the items of the same type. As reusing is more efficient when you compose items of similar structure, providing the content types ensures Compose doesn't try to compose an item of type A on top of a completely different item of type B. This helps maximize the benefits of composition reusing and your Lazy layout performance.

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

You can only reliably measure the performance of a Lazy layout when running in release mode and with R8 optimisation enabled. On debug builds, Lazy layout scrolling may appear slower. For more information on this, read through Compose performance .

{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}