Compose में पेजर

कॉन्टेंट को दाएं-बाएं या ऊपर-नीचे स्वाइप करने के लिए, HorizontalPager और VerticalPager कंपोज़ेबल का इस्तेमाल किया जा सकता है. इनके फ़ंक्शन, व्यू सिस्टम में मौजूद ViewPager के फ़ंक्शन जैसे ही होते हैं. डिफ़ॉल्ट रूप से, HorizontalPager पूरी स्क्रीन की चौड़ाई और VerticalPager पूरी स्क्रीन की ऊंचाई लेता है. पेजर, एक बार में सिर्फ़ एक पेज को फ़्लिंग करते हैं. इन सभी डिफ़ॉल्ट सेटिंग को कॉन्फ़िगर किया जा सकता है.

HorizontalPager

दाएं और बाएं स्वाइप करके स्क्रोल करने वाला पेजर बनाने के लिए, HorizontalPager का इस्तेमाल करें:

पहली इमेज. HorizontalPager
का डेमो

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

VerticalPager

ऊपर और नीचे स्वाइप करके स्क्रोल करने वाला पेजर बनाने के लिए, VerticalPager का इस्तेमाल करें:

दूसरी इमेज. VerticalPager
का डेमो

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

लेज़ी क्रिएशन

HorizontalPager और VerticalPager, दोनों में मौजूद पेजों को ज़रूरत पड़ने पर, लेज़ी कंपोज़ किया जाता है और लेआउट किया जाता है. जब उपयोगकर्ता पेजों के बीच स्क्रोल करता है, तो कंपोज़ेबल उन पेजों को हटा देता है जिनकी अब ज़रूरत नहीं होती.

स्क्रीन से बाहर मौजूद ज़्यादा पेज लोड करना

डिफ़ॉल्ट रूप से, पेजर सिर्फ़ स्क्रीन पर दिखने वाले पेजों को लोड करता है. स्क्रीन से बाहर मौजूद ज़्यादा पेज लोड करने के लिए, beyondBoundsPageCount को शून्य से ज़्यादा वैल्यू पर सेट करें.

पेजर में किसी आइटम पर स्क्रोल करना

पेजर में किसी खास पेज पर स्क्रोल करने के लिए, PagerState ऑब्जेक्ट बनाने के लिए rememberPagerState() का इस्तेमाल करें. इसके बाद, इसे पेजर के state पैरामीटर के तौर पर पास करें. ` CoroutineScope` में, इस स्टेट पर `PagerState#scrollToPage()` को कॉल किया जा सकता है:

val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp)
    )
}

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.scrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

अगर आपको पेज पर ऐनिमेशन दिखाना है, तो PagerState#animateScrollToPage() फ़ंक्शन का इस्तेमाल करें:

val pagerState = rememberPagerState(pageCount = {
    10
})

HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp)
    )
}

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.animateScrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

पेज की स्थिति में होने वाले बदलावों के बारे में सूचना पाना

PagerState में पेजों के बारे में जानकारी देने वाली तीन प्रॉपर्टी होती हैं: currentPage, settledPage, और targetPage.

  • currentPage: स्नैप पोज़िशन के सबसे पास वाला पेज. डिफ़ॉल्ट रूप से, स्नैप पोज़िशन लेआउट की शुरुआत में होती है.
  • settledPage: वह पेज नंबर जब कोई ऐनिमेशन या स्क्रोलिंग नहीं चल रही होती. यह currentPage प्रॉपर्टी से अलग है. ऐसा इसलिए, क्योंकि अगर पेज स्नैप पोज़िशन के काफ़ी पास है, तो currentPage तुरंत अपडेट हो जाता है. हालांकि, settledPage तब तक वही रहता है, जब तक सभी ऐनिमेशन पूरे नहीं हो जाते.
  • targetPage: स्क्रोलिंग मूवमेंट के लिए, सुझाई गई स्टॉप पोज़िशन.

snapshotFlow फ़ंक्शन का इस्तेमाल करके, इन वैरिएबल में होने वाले बदलावों को देखा जा सकता है और उन पर प्रतिक्रिया दी जा सकती है. उदाहरण के लिए, हर पेज में बदलाव होने पर, Analytics इवेंट भेजने के लिए, यह तरीका अपनाएं:

val pagerState = rememberPagerState(pageCount = {
    10
})

LaunchedEffect(pagerState) {
    // Collect from the a snapshotFlow reading the currentPage
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Do something with each page change, for example:
        // viewModel.sendPageSelectedEvent(page)
        Log.d("Page change", "Page changed to $page")
    }
}

VerticalPager(
    state = pagerState,
) { page ->
    Text(text = "Page: $page")
}

पेज इंडिकेटर जोड़ना

किसी पेज में इंडिकेटर जोड़ने के लिए, PagerState ऑब्जेक्ट का इस्तेमाल करके, यह जानकारी पाएं कि कई पेजों में से कौनसा पेज चुना गया है. इसके बाद, अपनी पसंद के मुताबिक इंडिकेटर बनाएं.

उदाहरण के लिए, सर्कल इंडिकेटर बनाने के लिए, pagerState.currentPage का इस्तेमाल करके, सर्कल की संख्या दोहराई जा सकती है. साथ ही, पेज चुने जाने पर सर्कल का रंग बदला जा सकता है:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    modifier = Modifier.fillMaxSize()
) { page ->
    // Our page content
    Text(
        text = "Page: $page",
    )
}
Row(
    Modifier
        .wrapContentHeight()
        .fillMaxWidth()
        .align(Alignment.BottomCenter)
        .padding(bottom = 8.dp),
    horizontalArrangement = Arrangement.Center
) {
    repeat(pagerState.pageCount) { iteration ->
        val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray
        Box(
            modifier = Modifier
                .padding(2.dp)
                .clip(CircleShape)
                .background(color)
                .size(16.dp)
        )
    }
}

कॉन्टेंट के नीचे, सर्कल इंडिकेटर दिखाने वाला पेजर
तीसरी इमेज. कॉन्टेंट के नीचे सर्कल इंडिकेटर दिखाने वाला पेजर

कॉन्टेंट पर आइटम स्क्रोल इफ़ेक्ट लागू करना

इसका एक सामान्य इस्तेमाल यह है कि पेजर आइटम पर इफ़ेक्ट लागू करने के लिए, स्क्रोल पोज़िशन का इस्तेमाल किया जाए. यह जानने के लिए कि कोई पेज, चुने गए पेज से कितनी दूरी पर है, आप PagerState.currentPageOffsetFraction का इस्तेमाल कर सकते हैं. इसके बाद, चुने गए पेज से दूरी के आधार पर, अपने कॉन्टेंट पर ट्रांसफ़ॉर्मेशन इफ़ेक्ट लागू किए जा सकते हैं.

चौथी इमेज. पेजर के कॉन्टेंट पर ट्रांसफ़ॉर्मेशन लागू करना

उदाहरण के लिए, आइटम के बीच की दूरी के आधार पर उनकी ओपैसिटी को अडजस्ट करने के लिए, सेंटर से उनकी दूरी के आधार पर आइटम की ओपैसिटी को अडजस्ट करने के लिए, पेजर में मौजूद किसी आइटम पर Modifier.graphicsLayer का इस्तेमाल करके, alpha में बदलाव करें:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(state = pagerState) { page ->
    Card(
        Modifier
            .size(200.dp)
            .graphicsLayer {
                // Calculate the absolute offset for the current page from the
                // scroll position. We use the absolute value which allows us to mirror
                // any effects for both directions
                val pageOffset = (
                    (pagerState.currentPage - page) + pagerState
                        .currentPageOffsetFraction
                    ).absoluteValue

                // We animate the alpha, between 50% and 100%
                alpha = lerp(
                    start = 0.5f,
                    stop = 1f,
                    fraction = 1f - pageOffset.coerceIn(0f, 1f)
                )
            }
    ) {
        // Card content
    }
}

पेज के कस्टम साइज़

डिफ़ॉल्ट रूप से, HorizontalPager पूरी चौड़ाई या VerticalPager पूरी ऊंचाई लेता है. आप pageSize वैरिएबल को Fixed, Fill (डिफ़ॉल्ट), या कस्टम साइज़ के हिसाब से सेट कर सकते हैं.

उदाहरण के लिए, 100.dp की तय चौड़ाई वाला पेज सेट करने के लिए:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    pageSize = PageSize.Fixed(100.dp)
) { page ->
    // page content
}

व्यू पोर्ट के साइज़ के आधार पर पेजों का साइज़ तय करने के लिए, पेज के साइज़ के हिसाब से कस्टम सेटिंग का इस्तेमाल करें. कस्टम PageSize ऑब्जेक्ट बनाएं और आइटम के बीच की दूरी को ध्यान में रखते हुए, availableSpace को तीन से भाग दें:

private val threePagesPerViewport = object : PageSize {
    override fun Density.calculateMainAxisPageSize(
        availableSpace: Int,
        pageSpacing: Int
    ): Int {
        return (availableSpace - 2 * pageSpacing) / 3
    }
}

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

HorizontalPager और VerticalPager, दोनों में कॉन्टेंट पैडिंग बदलने की सुविधा होती है. इससे पेजों के ज़्यादा से ज़्यादा साइज़ और अलाइनमेंट पर असर पड़ता है.

उदाहरण के लिए, start पैडिंग सेट करने से, पेज आखिर की ओर अलाइन हो जाते हैं:

स्टार्ट पैडिंग वाला पेजर, जिसमें कॉन्टेंट आखिर की ओर अलाइन किया गया है
पांचवी इमेज. स्टार्ट पैडिंग वाला पेजर.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(start = 64.dp),
) { page ->
    // page content
}

start और end पैडिंग, दोनों को एक ही वैल्यू पर सेट करने से, आइटम दाएं-बाएं के बीच में अलाइन हो जाता है:

कॉन्टेंट को बीच में दिखाने वाला, शुरू और आखिर में पैडिंग वाला पेजर
छठी इमेज. दाएं-बाएं पैडिंग वाला पेजर.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(horizontal = 32.dp),
) { page ->
    // page content
}

end पैडिंग सेट करने से, पेज शुरुआत की ओर अलाइन हो जाते हैं:

शुरू और आखिर में पैडिंग वाला पेजर, जिसमें कॉन्टेंट को शुरुआत में अलाइन किया गया है
सातवीं इमेज. एंड पैडिंग वाला पेजर.

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(end = 64.dp),
) { page ->
    // page content
}

VerticalPager के लिए भी इसी तरह के इफ़ेक्ट पाने के लिए, top और bottom वैल्यू सेट की जा सकती हैं. यहां 32.dp वैल्यू का इस्तेमाल सिर्फ़ एक उदाहरण के तौर पर किया गया है. पैडिंग के हर डाइमेंशन के लिए कोई भी वैल्यू सेट की जा सकती है.

स्क्रोल करने के तरीके को पसंद के मुताबिक बनाना

HorizontalPager और VerticalPager के डिफ़ॉल्ट कंपोज़ेबल में यह तय किया जाता है कि पेजर के साथ स्क्रोलिंग जेस्चर कैसे काम करते हैं. हालांकि, pagerSnapDistance या flingBehavior जैसे डिफ़ॉल्ट को पसंद के मुताबिक बनाया जा सकता है और बदला जा सकता है.

स्नैप की दूरी

डिफ़ॉल्ट रूप से, HorizontalPager और VerticalPager में, फ़्लिंग जेस्चर से एक बार में स्क्रोल किए जा सकने वाले पेजों की ज़्यादा से ज़्यादा संख्या एक पेज पर सेट होती है. इसे बदलने के लिए, pagerSnapDistance को flingBehavior पर सेट करें:

val pagerState = rememberPagerState(pageCount = { 10 })

val fling = PagerDefaults.flingBehavior(
    state = pagerState,
    pagerSnapDistance = PagerSnapDistance.atMost(10)
)

Column(modifier = Modifier.fillMaxSize()) {
    HorizontalPager(
        state = pagerState,
        pageSize = PageSize.Fixed(200.dp),
        beyondViewportPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}

अपने-आप आगे बढ़ने वाला पेजर बनाना

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

सामान्य जानकारी

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

@Composable
fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) {
    Box(modifier = Modifier.fillMaxSize()) {
        val pagerState = rememberPagerState(pageCount = { pageItems.size })
        val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState()

        val pageInteractionSource = remember { MutableInteractionSource() }
        val pageIsPressed by pageInteractionSource.collectIsPressedAsState()

        // Stop auto-advancing when pager is dragged or one of the pages is pressed
        val autoAdvance = !pagerIsDragged && !pageIsPressed

        if (autoAdvance) {
            LaunchedEffect(pagerState, pageInteractionSource) {
                while (true) {
                    delay(2000)
                    val nextPage = (pagerState.currentPage + 1) % pageItems.size
                    pagerState.animateScrollToPage(nextPage)
                }
            }
        }

        HorizontalPager(
            state = pagerState
        ) { page ->
            Text(
                text = "Page: $page",
                textAlign = TextAlign.Center,
                modifier = modifier
                    .fillMaxSize()
                    .background(pageItems[page])
                    .clickable(
                        interactionSource = pageInteractionSource,
                        indication = LocalIndication.current
                    ) {
                        // Handle page click
                    }
                    .wrapContentSize(align = Alignment.Center)
            )
        }

        PagerIndicator(pageItems.size, pagerState.currentPage)
    }
}

कोड के बारे में अहम बातें

  • AutoAdvancePager फ़ंक्शन, दाएं-बाएं स्वाइप करके स्क्रोल करने वाला व्यू बनाता है, जो अपने-आप आगे बढ़ता है. यह इनपुट के तौर पर, Color ऑब्जेक्ट की सूची लेता है. इनका इस्तेमाल हर पेज के बैकग्राउंड कलर के तौर पर किया जाता है.
  • pagerState को rememberPagerState का इस्तेमाल करके बनाया जाता है. इसमें पेजर की स्थिति सेव होती है .
  • pagerIsDragged और pageIsPressed, उपयोगकर्ता के इंटरैक्शन को ट्रैक करते हैं.
  • LaunchedEffect, पेजर को हर दो सेकंड में अपने-आप आगे बढ़ाता है. हालांकि, ऐसा तब तक होता है, जब तक उपयोगकर्ता पेजर को खींचता नहीं है या किसी पेज को दबाता नहीं है.
  • HorizontalPager , पेजों की सूची दिखाता है. इनमें से हर पेज में, पेज नंबर दिखाने वाला Text कंपोज़ेबल होता है. मॉडिफ़ायर, पेज को भरता है, pageItems से बैकग्राउंड कलर सेट करता है, और पेज को क्लिक किया जा सकने वाला बनाता है.

@Composable
fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) {
    Box(modifier = Modifier.fillMaxSize()) {
        Row(
            modifier = Modifier
                .wrapContentHeight()
                .fillMaxWidth()
                .align(Alignment.BottomCenter)
                .padding(bottom = 8.dp),
            horizontalArrangement = Arrangement.Center
        ) {
            repeat(pageCount) { iteration ->
                val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray
                Box(
                    modifier = modifier
                        .padding(2.dp)
                        .clip(CircleShape)
                        .background(color)
                        .size(16.dp)
                )
            }
        }
    }
}

कोड के बारे में अहम बातें

  • Box कंपोज़ेबल, रूट एलिमेंट के तौर पर काम करता है. इसमें पेज इंडिकेटर को दाएं-बाएं अलाइन करने के लिए, Row होता है.
  • कस्टम पेज इंडिकेटर, सर्कल की एक लाइन के तौर पर दिखता है. इसमें CircleShape पर क्लिप किया गया हर Box, एक पेज को दिखाता है.
  • मौजूदा पेज का सर्कल DarkGray रंग का होता है, जबकि अन्य सर्कल LightGray रंग के होते हैं. currentPageIndex पैरामीटर से यह तय होता है कि कौनसा सर्कल गहरे ग्रे रंग में दिखेगा.

नतीजा

इस वीडियो में, पहले दिए गए स्निपेट से, अपने-आप आगे बढ़ने वाला सामान्य पेजर दिखाया गया है:

आठवीं इमेज. अपने-आप आगे बढ़ने वाला पेजर. इसमें हर पेज के बीच दो सेकंड का डिले होता है.

अन्य संसाधन