پی‌جو در «نوشتن»

برای ورق زدن محتوا به‌صورت افقی یا عمودی، می‌توانید از ترکیب‌پذیرهای 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 به‌صورت تنبل ترکیب‌بندی می‌شوند و درصورت نیاز چیدمان می‌شوند. وقتی کاربر در صفحه‌ها پیمایش می‌کند، composable هر صفحه‌ای را که دیگر لازم نیست برمی‌دارد.

بار کردن صفحه‌های بیشتر در خارج از صفحه

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

شکل ۴. درحال اعمال تبدیل‌ها به محتوای «ورق‌زن»

برای مثال، برای تنظیم کردن میزان کدر بودن موارد براساس فاصله آن‌ها از مرکز، 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 حداکثر تعداد صفحاتی را که اشاره تند می‌تواند پیمایش کند روی یک صفحه در هر بار تنظیم می‌کنند. برای تغییر دادن این مورد، 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 برای مرتب کردن نشانگرهای صفحه به‌صورت افقی است.
  • نشانگر صفحه سفارشی به‌صورت ردیفی از دایره‌ها نمایش داده می‌شود که هرکدام Box به CircleShape برش داده شده است و نشان‌دهنده یک صفحه است.
  • دایره صفحه فعلی به رنگ DarkGray است، درحالی‌که دایره‌های دیگر LightGray هستند. پارامتر currentPageIndex تعیین می‌کند کدام دایره به‌رنگ خاکستری تیره نمایش داده شود.

نتیجه

This video displays the basic auto-advancing pager from the preceding snippets:

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

منابع بیشتر