پیجر در نوشتن

برای ورق زدن محتوا به صورت افقی یا عمودی، می‌توانید از Composableهای HorizontalPager و VerticalPager استفاده کنید. این Composableها عملکردهای مشابهی با ViewPager در سیستم نمایش دارند. به طور پیش‌فرض، HorizontalPager تمام عرض صفحه و VerticalPager تمام ارتفاع را اشغال می‌کند. همچنین، Pagerها فقط یک صفحه را در یک زمان جابجا می‌کنند. همه این مقادیر پیش‌فرض قابل تنظیم هستند.

پیجر افقی

برای ایجاد یک پیجر که به صورت افقی به چپ و راست اسکرول می‌کند، از HorizontalPager استفاده کنید:

شکل 1. نسخه نمایشی HorizontalPager

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

عمودی پیجر

برای ایجاد یک پیجر که به بالا و پایین اسکرول می‌کند، از VerticalPager استفاده کنید:

شکل 2. نسخه نمایشی 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 به صورت تنبل (lazyly) ترکیب و چیدمان می‌شوند و در صورت نیاز، طرح‌بندی می‌شوند. با اسکرول کردن کاربر در صفحات، صفحه‌ساز صفحاتی را که دیگر نیازی به آنها نیست، حذف می‌کند.

بارگذاری صفحات بیشتر خارج از صفحه

به طور پیش‌فرض، پیجر فقط صفحات قابل مشاهده روی صفحه را بارگذاری می‌کند. برای بارگذاری صفحات بیشتر خارج از صفحه، beyondBoundsPageCount روی مقداری بالاتر از صفر تنظیم کنید.

به یک مورد در صفحه‌نگار بروید

برای اسکرول کردن به یک صفحه خاص در صفحه‌بند، با استفاده از rememberPagerState() یک شیء PagerState ایجاد کنید و آن را به عنوان پارامتر state به صفحه‌بند منتقل کنید. می‌توانید PagerState#scrollToPage() در این حالت، درون یک CoroutineScope فراخوانی کنید:

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 : نزدیک‌ترین صفحه به موقعیت snap. به طور پیش‌فرض، موقعیت snap در ابتدای طرح‌بندی است.
  • settledPage : شماره صفحه زمانی که هیچ انیمیشن یا پیمایشی در حال اجرا نیست. این با ویژگی currentPage متفاوت است، زیرا currentPage بلافاصله اگر صفحه به اندازه کافی به موقعیت snap نزدیک باشد، به‌روزرسانی می‌شود، اما 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
}

برای اندازه‌بندی صفحات بر اساس اندازه‌ی viewport، از یک محاسبه‌ی اندازه‌ی صفحه‌ی سفارشی استفاده کنید. یک شیء PageSize سفارشی ایجاد کنید و availableSpace با در نظر گرفتن فاصله‌ی بین آیتم‌ها، بر سه تقسیم کنید:

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

پر کردن محتوا

HorizontalPager و VerticalPager هر دو از تغییر فاصله بین محتوا (content padding) پشتیبانی می‌کنند که به شما امکان می‌دهد حداکثر اندازه و ترازبندی صفحات را تنظیم کنید.

برای مثال، تنظیم فاصله start ، صفحات را به سمت انتها تراز می‌کند:

صفحه‌بندی با فاصله‌گذاری شروع که محتوا را به سمت انتها تراز می‌کند
شکل ۵. پیجر با فاصله‌گذاری شروع.

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

تنظیم مقدار یکسان برای padding start و end ، آیتم را به صورت افقی در مرکز قرار می‌دهد:

صفحه‌بند با حاشیه شروع و پایان که محتوا را در مرکز نشان می‌دهد
شکل 6. پیجر با حاشیه افقی.

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

تنظیم padding end ، صفحات را به سمت شروع تراز می‌کند:

صفحه‌بندی با حاشیه شروع و پایان که محتوا را با شروع تراز می‌کند
شکل 7. پیجر با پدگذاری انتهایی.

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

شما می‌توانید مقادیر top و bottom را برای دستیابی به جلوه‌های مشابه برای VerticalPager تنظیم کنید. مقدار 32.dp در اینجا فقط به عنوان مثال استفاده شده است؛ می‌توانید هر یک از ابعاد padding را روی هر مقداری تنظیم کنید.

سفارشی‌سازی رفتار اسکرول

کامپوننت‌های پیش‌فرض HorizontalPager و VerticalPager نحوه‌ی عملکرد حرکات اسکرول با پیجر را مشخص می‌کنند. با این حال، می‌توانید پیش‌فرض‌ها مانند pagerSnapDistance یا flingBehavior را سفارشی‌سازی و تغییر دهید.

فاصله اسنپ

به طور پیش‌فرض، HorizontalPager و VerticalPager حداکثر تعداد صفحاتی را که یک حرکت fling می‌تواند همزمان به یک صفحه پیمایش کند، تنظیم می‌کنند. برای تغییر این، 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 ایجاد می‌شود که وضعیت pager را در خود نگه می‌دارد.
  • pagerIsDragged و pageIsPressed تعامل کاربر را ردیابی می‌کنند.
  • LaunchedEffect به طور خودکار هر دو ثانیه صفحه را به جلو می‌برد، مگر اینکه کاربر صفحه را بکشد یا یکی از صفحات را فشار دهد.
  • HorizontalPager فهرستی از صفحات را نمایش می‌دهد که هر کدام دارای یک Text composable هستند که شماره صفحه را نمایش می‌دهد. این اصلاح‌کننده صفحه را پر می‌کند، رنگ پس‌زمینه را از 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 تعیین می‌کند که کدام دایره به رنگ خاکستری تیره نمایش داده شود.

نتیجه

این ویدیو، صفحه‌گردان خودکار اولیه را از قطعه کدهای قبلی نمایش می‌دهد:

شکل ۸. یک پیجر با قابلیت پیشروی خودکار با دو ثانیه تأخیر بین هر پیشروی صفحه.

منابع اضافی

{% کلمه به کلمه %} {% فعل کمکی %}