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 ফাংশন ব্যবহার করতে পারবেন । যেমন, প্রতিটি পৃষ্ঠা পরিবর্তনের সময় অ্যানালিটিক্স ইভেন্ট পাঠাতে, আপনি নিম্নলিখিত কাজগুলি করতে পারেন:

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 ব্যবহার করতে পারেন। তারপরে, বেছে নেওয়া পৃষ্ঠা থেকে দূরত্বের উপর ভিত্তি করে, আপনার কন্টেন্টে ট্রান্সফর্মেশন এফেক্ট প্রয়োগ করতে পারবেন।

ছবি ৪। Pager কন্টেন্টে ট্রান্সফর্মেশন প্রয়োগ করা

যেমন, কোনও আইটেম সেন্টার থেকে কতটা দূরে আছে তার উপর ভিত্তি করে সেটির ওপেসিটি অ্যাডজাস্ট করতে, পেজারের মধ্যে থাকা কোনও আইটেমে alpha ব্যবহার করে Modifier.graphicsLayer পরিবর্তন করুন:

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
}

আপনি top এবং bottom ভ্যালু সেট করে VerticalPager-এর জন্য একই ধরনের এফেক্ট পেতে পারেন। 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 কম্পোজ করার উপযুক্ত রুট এলিমেন্ট হিসেবে কাজ করে এবং পৃষ্ঠা নির্দেশকগুলিকে অনুভূমিকভাবে সাজাতে একটি Row থাকে।
  • কাস্টম পৃষ্ঠা নির্দেশক বৃত্তের একটি সারি হিসেবে দেখানো হয়, যেখানে প্রতিটি Box CircleShape-এর মধ্যে ক্লিপ করা একটি পৃষ্ঠা বোঝায়।
  • বর্তমান পৃষ্ঠার বৃত্তটি DarkGray হিসেবে রঙিন করা হয়েছে, যেখানে অন্যান্য বৃত্তগুলি LightGray হিসেবে দেখানো হয়েছে। currentPageIndex প্যারামিটার নির্ধারণ করে যে কোন বৃত্তটি গাঢ় ধূসর রঙে রেন্ডার হবে।

ফলাফল

এই ভিডিওতে আগের স্নিপেট থেকে নেওয়া প্রাথমিক অটো-অ্যাডভান্সিং পেজার দেখানো হয়েছে:

ছবি ৮. প্রতিটি পৃষ্ঠা প্রগ্রেসের মধ্যে দু'সেকেন্ডের বিলম্ব সহ একটি অটো-অ্যাডভান্সিং পেজার

অতিরিক্ত রিসোর্স