صفحه‌بندی با Compose برای Wear OS

صفحه‌بندی به کاربران امکان می‌دهد در دستگاه‌های Wear OS بین صفحه‌های تمام‌صفحه و متمایز به‌صورت افقی یا عمودی تند بکشند. موارد استفاده رایج شامل تند کشیدن بین سنجه‌های تمرین و کنترل‌های رسانه در برنامه تمرین، یا پیمایش گام‌به‌گام در جریان‌های چندصفحه‌ای است.

در «نوشتن» برای Wear OS Material 3،‏ HorizontalPagerScaffold و VerticalPagerScaffold چیدمان صفحه‌گرد را هماهنگ می‌کنند و به‌طور خودکار نشانگر صفحه را مکان‌یابی می‌کنند و انتقال‌ها را با TimeText مدیریت می‌کنند. هر صفحه در یک عنصر ترکیبی AnimatedPage پیچیده شده است که جلوه‌های مقیاس‌بندی، تغییر شکل گوشه‌های گرد، و سایه را هنگام انتقال صفحات در نمایشگر گرد اعمال می‌کند.

پویانمایی زیر نشان می‌دهد که چگونه HorizontalPagerScaffold و AnimatedPage صفحه‌ها را درحین تند کشیدن افقی مقیاس‌بندی و پویانمایی می‌کنند:

سلسله‌مراتب داربست برای صفحه‌بندی

هنگام طراحی چیدمان‌های صفحه چندصفحه‌ای در Material 3—چه تند به چپ یا راست بکشید (HorizontalPager) یا تند به بالا یا پایین بکشید (VerticalPager)—از سلسله مراتب عنصر زیر از محتوی بیرونی تا محتوای درونی استفاده کنید:

  1. AppScaffold: بیرونی‌ترین محتوی در ریشه برنامه شما (فقط یکی در هر برنامه استفاده کنید). این گزینه رونهاد جهانی TimeText را لنگر می‌زند تا ساعت درطول انتقال صفحه و اشاره‌های تند به‌سمت بستن در بالای صفحه ثابت بماند.
  2. HorizontalPagerScaffold یا VerticalPagerScaffold: در سطح صفحه‌بندی در AppScaffold قرار می‌گیرد. این ویژگی انتقال بین TimeText و HorizontalPageIndicator یا VerticalPageIndicator را هماهنگ می‌کند.
  3. HorizontalPager یا VerticalPager: محتوی صفحه‌بندی پایه که حرکات تند کشیدن، فیزیک پرتاب، و چسباندن ورودی چرخشی را بااستفاده از PagerState مشترک مدیریت می‌کند.
  4. AnimatedPage: در لامبدای محتوای صفحه صفحه‌بندی قرار می‌گیرد. هر صفحه را می‌پیچد تا پویانمایی‌های انتقال Material 3 (جلوه‌های مقیاس‌بندی و سایه) را براساس انحراف صفحه در PagerState اعمال کند.
  5. ScreenScaffold: درون AnimatedPage برای هر صفحه مجزا قرار می‌گیرد. ازآنجایی‌که هر صفحه می‌تواند حاوی فهرست پیمایش‌پذیر عمودی (مثل TransformingLazyColumn) یا EdgeButton خودش باشد، قرار دادن ScreenScaffold در داخل هر صفحه تضمین می‌کند که نشانگرهای پیمایش عمودی، دکمه‌های لبه، و contentPadding متعلق به آن صفحه خاص باشند و به‌آرامی با آن پویانمایی شوند.
AppScaffold (1 per app: anchors global TimeText)
 └── HorizontalPagerScaffold / VerticalPagerScaffold (manages PageIndicator)
      └── HorizontalPager / VerticalPager (manages PagerState & fling behavior)
           └── AnimatedPage (applies scaling & scrim transitions per page)
                └── ScreenScaffold (1 per page: ScrollIndicator & EdgeButton)
                     └── Page Content (Column or TransformingLazyColumn)

پیاده‌سازی صفحه‌بندی افقی

برای پیاده‌سازی صفحه‌گرد افقی در Material 3، HorizontalPagerScaffold، HorizontalPager، AnimatedPage، و ScreenScaffold را در AppScaffold برنامه خود تودرتو کنید.

از PagerScaffoldDefaults.snapWithSpringFlingBehavior برای flingBehavior استفاده کنید تا فیزیک حرکت فنری «طراحی مواد ۳» و چسبیدن صفحه پاسخ‌گو اعمال شود (HighSnapPositionalThreshold). به‌طور پیش‌فرض، HorizontalPager پیمایش صفحه چرخشی (rotaryScrollableBehavior = null) را غیرفعال می‌کند و به ورودی تاجک یا قاب چرخان اجازه می‌دهد فهرست‌های عمودی (مثل TransformingLazyColumn) را در صفحه فعال پیمایش کند.

نمونه زیر راه‌اندازی کامل HorizontalPagerScaffold را نشان می‌دهد:

@Composable
fun HorizontalPagerScaffoldSample(navigateBack: () -> Unit) {
    AppScaffold {
        val pagerState = rememberPagerState(pageCount = { 10 })

        HorizontalPagerScaffold(pagerState = pagerState) {
            HorizontalPager(
                state = pagerState,
                flingBehavior =
                    PagerScaffoldDefaults.snapWithSpringFlingBehavior(
                        state = pagerState
                    ),
            ) { page ->
                AnimatedPage(pageIndex = page, pagerState = pagerState) {
                    ScreenScaffold {
                        Column(
                            modifier = Modifier.fillMaxSize(),
                            horizontalAlignment = Alignment.CenterHorizontally,
                            verticalArrangement = Arrangement.Center,
                        ) {
                            Text(text = "Page #$page")
                            Spacer(modifier = Modifier.height(8.dp))
                            Text(text = "Swipe left and right")
                            if (page == 0) {
                                Spacer(modifier = Modifier.height(16.dp))
                                Button(onClick = navigateBack) { Text("Exit") }
                            }
                        }
                    }
                }
            }
        }
    }
}

اگر صفحه‌ای در صفحه‌بندی افقی شما حاوی TransformingLazyColumn است، rememberTransformingLazyColumnState() جداگانه‌ای در محدوده آن صفحه ایجاد کنید و آن را به ScreenScaffold و TransformingLazyColumn داخلی منتقل کنید:

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })

    HorizontalPagerScaffold(pagerState = pagerState) {
        HorizontalPager(
            state = pagerState,
            flingBehavior =
                PagerScaffoldDefaults.snapWithSpringFlingBehavior(
                    state = pagerState
                ),
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                val columnState = rememberTransformingLazyColumnState()
                val transformationSpec = rememberTransformationSpec()

                ScreenScaffold(
                    scrollState = columnState,
                ) { contentPadding ->
                    TransformingLazyColumn(
                        state = columnState,
                        contentPadding = contentPadding,
                    ) {
                        item {
                            ListHeader(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .transformedHeight(this, transformationSpec)
                                    .minimumVerticalContentPadding(
                                        ListHeaderDefaults.minimumTopListContentPadding
                                    ),
                                transformation = SurfaceTransformation(transformationSpec),
                            ) {
                                Text(text = "Pager sample")
                            }
                        }
                        item {
                            Card(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .transformedHeight(this, transformationSpec)
                                    .minimumVerticalContentPadding(
                                        CardDefaults.minimumVerticalListContentPadding
                                    ),
                                transformation = SurfaceTransformation(transformationSpec),
                            ) {
                                if (page == 0) {
                                    Text(text = "Page #$page. Swipe right")
                                } else {
                                    Text(text = "Page #$page. Swipe left and right")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

پیاده‌سازی صفحه پیمایش عمودی

صفحه‌گرد عمودی به کاربران امکان می‌دهد بین صفحات تند به‌بالا و پایین بکشند یا بااستفاده از دکمه کناری چرخان یا قاب ساعت، در آن‌ها گام‌به‌گام حرکت کنند.

برخلاف HorizontalPager، VerticalPager به‌طور پیش‌فرض چسباندن صفحه چرخشی را فعال می‌کند (RotaryScrollableDefaults.snapBehavior(state)). PagerScaffoldDefaults.snapWithSpringFlingBehavior را به flingBehavior منتقل کنید و هر صفحه را در AnimatedPage و ScreenScaffold بپیچید:

@Composable
fun VerticalPagerScaffoldSample() {
    AppScaffold {
        val pagerState = rememberPagerState(pageCount = { 10 })

        VerticalPagerScaffold(pagerState = pagerState) {
            VerticalPager(
                state = pagerState,
                flingBehavior =
                    PagerScaffoldDefaults.snapWithSpringFlingBehavior(
                        state = pagerState
                    ),
            ) { page ->
                AnimatedPage(pageIndex = page, pagerState = pagerState) {
                    ScreenScaffold {
                        Column(
                            modifier = Modifier.fillMaxSize(),
                            horizontalAlignment = Alignment.CenterHorizontally,
                            verticalArrangement = Arrangement.Center,
                        ) {
                            Text(text = "Page #$page")
                            Spacer(modifier = Modifier.height(8.dp))
                            Text(text = "Swipe up and down")
                        }
                    }
                }
            }
        }
    }
}

سفارشی‌سازی رفتار پیجر

می‌توانید حساسیت چسباندن HorizontalPagerScaffold و VerticalPagerScaffold را سفارشی‌سازی کنید تا با نیازهای تعاملی برنامه‌تان مطابقت داشته باشد.

تنظیم حساسیت چسبندگی برای تمرین‌ها

درطول تمرینات ورزشی یا فعالیت‌های پرتحرک که کنترل حرکات درشت کاربر محدود می‌شود، لمس‌های تصادفی یا چرخش‌های جزئی تاج می‌تواند به‌طور ناخواسته صفحات را تغییر دهد. برای این صفحه‌ها، صفحه‌جوی خود را با حساسیت پایین پیکربندی کنید:

  • برای رد کردن PagerDefaults.snapFlingBehavior با maxFlingPages = 0 و snapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThreshold باید انگشتتان را به‌صورت عمدی روی بخش بزرگ‌تری از صفحه بکشید تا صفحه ورق بخورد.
  • اگر چسباندن صفحه چرخشی را فعال کنید، snapSensitivity = RotaryScrollableDefaults.LowSnapSensitivity را در RotaryScrollableDefaults.snapBehavior تنظیم کنید تا قبل‌از چسباندن به صفحه بعدی، چرخش بیشتری لازم باشد.
@Composable
fun HorizontalPagerScaffoldWithLowSensitivitySample(navigateBack: () -> Unit) {
    AppScaffold {
        val pagerState = rememberPagerState(pageCount = { 3 })

        HorizontalPagerScaffold(pagerState = pagerState) {
            HorizontalPager(
                state = pagerState,
                flingBehavior =
                    PagerDefaults.snapFlingBehavior(
                        state = pagerState,
                        maxFlingPages = 0,
                        snapPositionalThreshold =
                            PagerScaffoldDefaults.LowSnapPositionalThreshold,
                    ),
                rotaryScrollableBehavior =
                    RotaryScrollableDefaults.snapBehavior(
                        pagerState = pagerState,
                        snapSensitivity =
                            RotaryScrollableDefaults.LowSnapSensitivity,
                    ),
            ) { page ->
                AnimatedPage(pageIndex = page, pagerState = pagerState) {
                    ScreenScaffold {
                        // Page content
                    }
                }
            }
        }
    }
}

برای پیاده‌سازی‌های نمونه کامل، Pager.kt را در android/snippets مخزن و PagerScaffoldSample.kt را در مخزن AndroidX ببینید.