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 को शून्य से ज़्यादा की वैल्यू पर सेट करें.

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

पेजर में किसी पेज पर स्क्रोल करने के लिए, rememberPagerState() का इस्तेमाल करके एक PagerState ऑब्जेक्ट बनाएं और उसे पेजर में 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 सेट करते हैं कि एक बार में कितने पेज तक फ़्लिंग जेस्चर से स्क्रोल किया जा सकता है. इसे बदलने के लिए, flingBehavior पर pagerSnapDistance सेट करें:

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 कॉम्पोज़ेबल का इस्तेमाल रूट एलिमेंट के तौर पर किया जाता है.
    • Box में, Row कॉम्पोज़ेबल, पेज इंडिकेटर को हॉरिज़ॉन्टल तौर पर व्यवस्थित करता है.
  • कस्टम पेज इंडिकेटर, सर्कल की एक पंक्ति के तौर पर दिखता है. इसमें, Box पर क्लिप किए गए हर circle से एक पेज का पता चलता है.
  • मौजूदा पेज के सर्कल का रंग DarkGray है, जबकि अन्य सर्कल का रंग LightGray है. currentPageIndex पैरामीटर से यह तय होता है कि कौनसा सर्कल, डार्क ग्रे में रेंडर होगा.

नतीजा

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

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