लिस्ट और ग्रिड

कई ऐप्लिकेशन को आइटम के कलेक्शन दिखाने की ज़रूरत होती है. इस दस्तावेज़ में बताया गया है कि Jetpack Compose में, इसे बेहतर तरीके से कैसे किया जा सकता है.

अगर आपको पता है कि आपके इस्तेमाल के उदाहरण के लिए स्क्रोल करने की ज़रूरत नहीं है, तो निर्देश के आधार पर, किसी आसान Column या Row का इस्तेमाल करें. साथ ही, हर आइटम का कॉन्टेंट सूची में नीचे दिए गए तरीके से दोहराना:

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

हम verticalScroll() मॉडिफ़ायर का इस्तेमाल करके, Column को स्क्रोल करने लायक बना सकते हैं.

लेज़ी लिस्ट

अगर आपको बहुत ज़्यादा आइटम (या किसी ऐसी सूची को दिखाना है जिसकी लंबाई का पता नहीं है), तो Column जैसे लेआउट का इस्तेमाल करने पर परफ़ॉर्मेंस से जुड़ी समस्याएं आ सकती हैं. इसकी वजह यह है कि सभी आइटम को कॉम्पोज़ और लेआउट किया जाएगा, भले ही वे दिखते हों या नहीं.

Compose, कॉम्पोनेंट का एक सेट उपलब्ध कराता है. इससे सिर्फ़ ऐसे आइटम तैयार होते हैं और लेआउट किए जाते हैं कॉम्पोनेंट के व्यूपोर्ट में दिखती हैं. इन कॉम्पोनेंट में LazyColumn और LazyRow शामिल हैं.

जैसा कि नाम से ही पता चलता है, LazyColumn और LazyRow वह ओरिएंटेशन है जिसमें वे अपने आइटम लेआउट करते हैं और स्क्रोल करते हैं. LazyColumn वर्टिकल स्क्रोलिंग सूची बनाता है और LazyRow हॉरिज़ॉन्टल तौर पर स्क्रोल करता है स्क्रोलिंग सूची.

Compose में ज़्यादातर लेआउट से लेज़ी कॉम्पोनेंट अलग होते हैं. लेज़ी कॉम्पोनेंट, @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 कॉम्पोनेंट, आइटम को ग्रिड में दिखाने की सुविधा देते हैं. लेज़ी वर्टिकल ग्रिड अपने आइटम को वर्टिकल तौर पर स्क्रोल किए जा सकने वाले कंटेनर में दिखाएगा, जो एक से ज़्यादा कॉलम में वैल्यू होंगी, जबकि लेज़ी हॉरिज़ॉन्टल ग्रिड का व्यवहार एक जैसा होगा आदी है.

ग्रिड में, सूचियों जैसी ही बेहतर API सुविधाएं होती हैं. साथ ही, वे कॉन्टेंट के बारे में बताने के लिए, एक जैसे डीएसएल - LazyGridScope.() का इस्तेमाल करते हैं.

फ़ोटो का ग्रिड दिखाने वाले फ़ोन का स्क्रीनशॉट

LazyVerticalGrid में मौजूद columns पैरामीटर और LazyHorizontalGrid में मौजूद rows पैरामीटर से यह कंट्रोल होता है कि सेल को कॉलम या पंक्तियों में कैसे बनाया जाए. नीचे दिए गए उदाहरण, आइटम को ग्रिड में दिखाता है. GridCells.Adaptive हर कॉलम की चौड़ाई कम से कम 128.dp पर सेट करने के लिए:

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

LazyVerticalGrid की मदद से, आइटम की चौड़ाई तय की जा सकती है. इसके बाद, ग्रिड में ज़्यादा से ज़्यादा कॉलम फ़िट हो जाएंगे. कॉलम की संख्या का हिसाब लगाने के बाद, बाकी बची चौड़ाई को कॉलम के बीच बराबर बांटा जाता है. साइज़ तय करने का यह अडैप्टिव तरीका, अलग-अलग स्क्रीन साइज़ पर आइटम के सेट दिखाने के लिए खास तौर पर मददगार होता है.

अगर आपको इस्तेमाल किए जाने वाले कॉलम की सही संख्या पता है, तो का इंस्टेंस GridCells.Fixed जिसमें ज़रूरी कॉलम की संख्या मौजूद है.

अगर आपके डिज़ाइन में सिर्फ़ कुछ आइटम के लिए स्टैंडर्ड डाइमेंशन की ज़रूरत है, तो आइटम के लिए कस्टम कॉलम स्पैन देने के लिए, ग्रिड सपोर्ट का इस्तेमाल किया जा सकता है. कॉलम अवधि इस विकल्प के span पैरामीटर के साथ तय करें LazyGridScope DSL item और items तरीके. maxLineSpan, स्पैन स्कोप की एक वैल्यू, खास तौर पर तब काम आती है, जब अडैप्टिव साइज़िंग की सुविधा, क्योंकि कॉलम की संख्या तय नहीं होती है. इस उदाहरण में, लाइन का पूरा हिस्सा देने का तरीका बताया गया है:

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

लेज़ी स्टेजर्ड ग्रिड

LazyVerticalStaggeredGrid और LazyHorizontalStaggeredGrid ऐसे कंपोज़ेबल होते हैं जिनकी मदद से आप आइटम के लेज़ी-लोडेड, स्टैग किए गए ग्रिड बना सकते हैं. लेज़ी वर्टिकल स्टैगर्ड ग्रिड, अपने आइटम को वर्टिकल तौर पर स्क्रोल किए जा सकने वाले कंटेनर में दिखाता है. यह कंटेनर कई कॉलम में फैला होता है और अलग-अलग आइटम की ऊंचाई अलग-अलग हो सकती है. लेज़ी हॉरिज़ॉन्टल ग्रिड, अलग-अलग चौड़ाई वाले आइटम वाला हॉरिज़ॉन्टल ऐक्सिस.

यहां दिया गया स्निपेट, हर आइटम के लिए 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()
)

पहली इमेज. लेज़ी स्टेज किए गए वर्टिकल ग्रिड का उदाहरण

कॉलम की तय संख्या सेट करने के लिए, 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()
)
Compose में इमेज की लेज़ी स्टैग की गई ग्रिड
दूसरी इमेज. तय किए गए कॉलम के साथ लेज़ी स्टेज किए गए वर्टिकल ग्रिड का उदाहरण

कॉन्टेंट पैडिंग

कभी-कभी आपको कॉन्टेंट के किनारों पर पैडिंग जोड़नी पड़ सकती है. लैज़ी कॉम्पोनेंट की मदद से, contentPadding पैरामीटर में कुछ PaddingValues पास किए जा सकते हैं, ताकि यह काम कर सके:

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

इस उदाहरण में, हम हॉरिज़ॉन्टल किनारों पर पैडिंग (जगह) के 16.dp को जोड़ते हैं (बाएं और दायां) और फिर कॉन्टेंट के सबसे ऊपर और सबसे नीचे 8.dp.

कृपया ध्यान दें कि यह पैडिंग कॉन्टेंट पर लागू होती है, न कि LazyColumn खुद. ऊपर दिए गए उदाहरण में, पहले आइटम के ऊपर 8.dp padding जोड़ा जाएगा, आखिरी आइटम के नीचे 8.dp padding जोड़ा जाएगा, और सभी आइटम के बाईं और दाईं ओर 16.dp padding जोड़ा जाएगा.

कॉन्टेंट के बीच स्पेस

आइटम के बीच स्पेस जोड़ने के लिए, इनका इस्तेमाल किया जा सकता है 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)
    }
}

कुंजियां उपलब्ध कराने से, Compose की सुविधा में आपके ऑर्डर का क्रम सही से मैनेज करने में मदद मिलती है. उदाहरण के लिए, अगर आपके आइटम में याद रखी गई स्थिति शामिल है, तो कुंजियों को सेट करने से जब आइटम की स्थिति बदल जाती है, तो इस स्थिति को आइटम के साथ ले जाने के लिए लिखें.

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

हालांकि, आइटम की कुंजियों के तौर पर कुछ खास तरह के आइटम का इस्तेमाल किया जा सकता है. कुंजी के टाइप के साथ काम करना ज़रूरी है Bundle, Android का वह तरीका जो यह जानकारी दिखाता है कि गतिविधि को कब फिर से बनाया गया. Bundle, प्रिमिटिव जैसे टाइप के साथ काम करता है enum या पार्सलेबल.

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

यह ज़रूरी है कि बटन, Bundle के साथ काम करता हो, ताकि ऐक्टिविटी को फिर से बनाने पर, आइटम कॉम्पोज़ेबल में मौजूद rememberSaveable को वापस लाया जा सके. इसके अलावा, इस आइटम से स्क्रोल करके वापस आने पर भी, 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)
            )
        ) {
            // ...
        }
    }
}

पक्का करें कि आपने अपने आइटम के लिए कुंजियां दी हों, ताकि मूव किए गए एलिमेंट की नई जगह ढूंढी जा सके.

स्टिकी हेडर (प्रयोग के तौर पर उपलब्ध)

ग्रुप किए गए डेटा की सूचियां दिखाते समय, 'स्टिकी हेडर' पैटर्न मददगार होता है. नीचे 'संपर्क सूची' का एक उदाहरण दिया गया है, जिसे हर संपर्क की संपर्क सूची के हिसाब से ग्रुप में बांटा गया है नाम का पहला अक्षर:

फ़ोन का वीडियो, जिसमें संपर्क सूची में ऊपर और नीचे की ओर स्क्रोल करते हुए दिखाया गया है

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

कंपोज़िशन में स्थिति को सीधे तौर पर पढ़ना तब फ़ायदेमंद होता है, जब आपको अपडेट करना हो अन्य यूज़र इंटरफ़ेस (यूआई) कंपोज़ेबल. हालांकि, कुछ मामलों में ऐसे भी उदाहरण हो सकते हैं जहां इवेंट की ज़रूरत न हो एक ही कंपोज़िशन में हैंडल किया जाना चाहिए. इसका एक सामान्य उदाहरण यह है कि जब उपयोगकर्ता किसी खास पॉइंट तक स्क्रोल कर लेता है, तो Analytics इवेंट भेजा जाता है. इसे बेहतर तरीके से मैनेज करने के लिए, हम snapshotFlow() का इस्तेमाल कर सकते हैं:

val listState = rememberLazyListState()

LazyColumn(state = listState) {
    // ...
}

LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .map { index -> index > 0 }
        .distinctUntilChanged()
        .filter { it }
        .collect {
            MyAnalyticsService.sendScrolledPastFirstItemEvent()
        }
}

LazyListState, layoutInfo प्रॉपर्टी के ज़रिए, स्क्रीन पर दिख रहे सभी आइटम और उनके बॉउंड की जानकारी भी देता है. ज़्यादा जानकारी के लिए, LazyListLayoutInfo क्लास देखें.

स्क्रोल की पोज़िशन को कंट्रोल करना

स्क्रोल की स्थिति पर प्रतिक्रिया देने के साथ-साथ, ऐप्लिकेशन के लिए स्क्रोल की स्थिति को कंट्रोल करना भी मददगार होता है. LazyListState इसके लिए, scrollToItem() फ़ंक्शन का इस्तेमाल किया जाता है. यह फ़ंक्शन, स्क्रॉल की पोज़िशन को 'तुरंत' स्नैप करता है. साथ ही, animateScrollToItem() ऐनिमेशन (इसे स्मूद स्क्रॉल भी कहा जाता है) का इस्तेमाल करके स्क्रॉल करता है:

@Composable
fun MessageList(messages: List<Message>) {
    val listState = rememberLazyListState()
    // Remember a CoroutineScope to be able to launch
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(state = listState) {
        // ...
    }

    ScrollToTopButton(
        onClick = {
            coroutineScope.launch {
                // Animate scroll to the first item
                listState.animateScrollToItem(index = 0)
            }
        }
    )
}

बड़े डेटा-सेट (पेजिंग)

पेजिंग लाइब्रेरी की मदद से, ऐप्लिकेशन में आइटम की बड़ी सूचियां दिखाई जा सकती हैं. साथ ही, ज़रूरत के हिसाब से सूची के छोटे हिस्से लोड और दिखाए जा सकते हैं. Paging 3.0 और उसके बाद के वर्शन में, androidx.paging:paging-compose लाइब्रेरी की मदद से Compose की सुविधा मिलती है.

पेज किए गए कॉन्टेंट की सूची दिखाने के लिए, हम collectAsLazyPagingItems() एक्सटेंशन फ़ंक्शन का इस्तेमाल कर सकते हैं. इसके बाद, LazyColumn में LazyPagingItems को items() में पास कर सकते हैं. व्यू में पेजिंग की सुविधा की तरह ही, डेटा लोड होने के दौरान प्लेसहोल्डर दिखाए जा सकते हैं. इसके लिए, यह देखें कि item null है या नहीं:

@Composable
fun MessageList(pager: Pager<Int, Message>) {
    val lazyPagingItems = pager.flow.collectAsLazyPagingItems()

    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it.id }
        ) { index ->
            val message = lazyPagingItems[index]
            if (message != null) {
                MessageRow(message)
            } else {
                MessagePlaceholder()
            }
        }
    }
}

लेज़ी लेआउट का इस्तेमाल करने से जुड़ी सलाह

यहां कुछ सुझाव दिए गए हैं. इनकी मदद से, यह पक्का किया जा सकता है कि आपके लेज़ी लेआउट सही तरीके से काम करें.

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

एक आइटम में एक से ज़्यादा एलिमेंट डालने से बचें

इस उदाहरण में, दूसरा आइटम lambda एक ब्लॉक में दो आइटम का उत्सर्जन करता है:

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

लेज़ी लेआउट, इस काम को उम्मीद के मुताबिक करेंगे - वे एलिमेंट को एक के बाद एक इस तरह से लेआउट करेंगे जैसे कि वे अलग-अलग आइटम हों. हालांकि, ऐसा करने में कुछ समस्याएं आ सकती हैं.

जब किसी एक आइटम के हिस्से के रूप में एक से ज़्यादा एलिमेंट उत्सर्जित किए जाते हैं, तो उन्हें इस तरह हैंडल किया जाता है का मतलब है कि अब उन्हें अलग-अलग नहीं बनाया जा सकता. अगर स्क्रीन पर एक एलिमेंट दिखता है, तो उस आइटम से जुड़े सभी एलिमेंट को कंपोज और मेज़र करना होगा. इसका इस्तेमाल करने पर, परफ़ॉर्मेंस पर बुरा असर पड़ सकता है बहुत ज़्यादा. सभी तत्वों को एक ही आइटम में रखने की स्थिति में, लेज़ी लेआउट के इस्तेमाल को पूरी तरह से नाकाम कर देता है. परफ़ॉर्मेंस से जुड़ी संभावित समस्याओं के अलावा, एक आइटम में ज़्यादा एलिमेंट डालने से scrollToItem() और animateScrollToItem() में भी रुकावट आएगी.

हालांकि, एक आइटम में एक से ज़्यादा एलिमेंट रखने के मान्य उदाहरण हैं, जैसे कि किसी सूची में डिवाइडर होने चाहिए. आपको डिवाइडर से स्क्रोलिंग इंडेक्स में बदलाव नहीं कराना है, क्योंकि उन्हें अलग एलिमेंट नहीं माना जाना चाहिए. साथ ही, डिवाइडर छोटे होने की वजह से परफ़ॉर्मेंस पर असर नहीं पड़ेगा. डिवाइडर को आइटम के दिखने से पहले ही दिखेगा, ताकि वे पिछले स्टोर का हिस्सा हो सकें आइटम:

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

पसंद के मुताबिक सेटिंग चुनें

आम तौर पर, लेज़ी लिस्ट में कई आइटम होते हैं और वे स्क्रोल करने वाला कंटेनर. हालांकि, जब आपकी सूची में कुछ आइटम भरे जाते हैं, तो आपके डिज़ाइन की शर्तों और दिशा-निर्देशों को लागू करने के लिए, दिखाई देती है.

ऐसा करने के लिए, कस्टम वर्टिकल Arrangement का इस्तेमाल करें और उसे LazyColumn में पास करें. नीचे दिए गए उदाहरण में, TopWithFooter ऑब्जेक्ट को सिर्फ़ arrange तरीका लागू करने की ज़रूरत है. सबसे पहले, यह आइटम को एक के बाद एक व्यवस्थित करेगा. दूसरा, अगर इस्तेमाल की गई कुल ऊंचाई व्यूपोर्ट की ऊंचाई, यह फ़ुटर को सबसे नीचे स्थान देगा:

object TopWithFooter : Arrangement.Vertical {
    override fun Density.arrange(
        totalSize: Int,
        sizes: IntArray,
        outPositions: IntArray
    ) {
        var y = 0
        sizes.forEachIndexed { index, size ->
            outPositions[index] = y
            y += size
        }
        if (y < totalSize) {
            val lastIndex = outPositions.lastIndex
            outPositions[lastIndex] = totalSize - sizes.last()
        }
    }
}

contentType जोड़ें

Compose 1.2 से, अपने लेज़ी लेआउट की परफ़ॉर्मेंस को बेहतर बनाने के लिए, अपनी सूचियों या ग्रिड में contentType जोड़ें. इसकी मदद से, लेआउट के हर आइटम के लिए कॉन्टेंट टाइप तय किया जा सकता है. ऐसा तब किया जाता है, जब कई तरह के आइटम वाली सूची या ग्रिड बनाई जा रही हो:

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

contentType का इस्तेमाल करने पर, Compose सिर्फ़ एक ही तरह के आइटम के बीच कॉम्पोज़िशन का फिर से इस्तेमाल कर सकता है. दरअसल, ट्रेंडिंग पेज का दोबारा इस्तेमाल करना ज़्यादा कारगर होता है, मिलते-जुलते स्ट्रक्चर वाले आइटम तैयार करते हैं. साथ ही, कॉन्टेंट के टाइप देना पक्का करता है लिखें सुविधा, पूरी तरह से A प्रकार के आइटम को लिखने का प्रयास नहीं करती है B टाइप के अलग-अलग आइटम हैं. इससे कंपोज़िशन के फ़ायदों को बढ़ाने में मदद मिलती है लेज़ी लेआउट का इस्तेमाल करें.

परफ़ॉर्मेंस मेज़र करना

रिलीज़ मोड में और R8 ऑप्टिमाइज़ेशन चालू होने पर ही, लेज़ी लेआउट की परफ़ॉर्मेंस को सही तरीके से मेज़र किया जा सकता है. डीबग बिल्ड पर, लेज़ी लेआउट स्क्रोल करने की रफ़्तार धीमी हो सकती है. इस बारे में ज़्यादा जानकारी के लिए, इसे पढ़ें परफ़ॉर्मेंस लिखें.