تقسيم المحتوى إلى صفحات باستخدام Compose for Wear OS

تتيح ميزة "تقسيم المحتوى إلى صفحات" للمستخدمين التمرير سريعًا بشكل أفقي أو عمودي بين صفحات منفصلة بملء الشاشة على أجهزة Wear OS. وتشمل حالات الاستخدام الشائعة التمرير سريعًا بين مقاييس التمرين وعناصر التحكّم في الوسائط في تطبيق رياضي، أو التنقّل بين التدفقات المتعددة الصفحات.

في Compose for Wear OS Material 3، ينسّق HorizontalPagerScaffold وVerticalPagerScaffold تخطيط أداة عرض الصفحات، ويحدّدان تلقائيًا موضع مؤشر الصفحة ويديران عمليات الانتقال باستخدام TimeText. يتم تضمين كل صفحة في عنصر AnimatedPage قابل للإنشاء، ويتم تطبيق تأثيرات تغيير الحجم وتغيير شكل الزوايا المستديرة وتأثيرات التعتيم أثناء انتقال الصفحات على الشاشة الدائرية.

تعرض الصورة المتحركة التالية كيفية HorizontalPagerScaffoldAnimatedPage تغيير حجم الصفحات وتحريكها أثناء التمرير السريع أفقيًا:

التسلسل الهرمي للرمز الأولي الخاص بالتقسيم إلى صفحات

عند تصميم تنسيقات شاشة مقسّمة إلى صفحات في Material 3، سواء كان ذلك من خلال التمرير السريع لليسار أو لليمين (HorizontalPager) أو للأعلى أو للأسفل (VerticalPager)، استخدِم التسلسل الهرمي التالي للمكوّنات من الحاوية الخارجية إلى المحتوى الداخلي:

  1. AppScaffold: الحاوية الخارجية في جذر تطبيقك (استخدِم حاوية واحدة فقط لكل تطبيق). يتم تثبيت تراكب TimeText الشامل حتى تظل الساعة ثابتة في أعلى الشاشة أثناء عمليات انتقال الصفحات وإيماءات التمرير السريع للإغلاق.
  2. HorizontalPagerScaffold أو VerticalPagerScaffold: يتم وضعها داخل AppScaffold على مستوى أداة تقسيم المحتوى إلى صفحات. وينسّق عمليات الانتقال بين TimeText وHorizontalPageIndicator أو VerticalPageIndicator.
  3. HorizontalPager أو VerticalPager: حاوية أداة عرض الصفحات الأساسية التي تدير إيماءات التمرير السريع، وقوانين الفيزياء الخاصة بالحركة السريعة، وميزة المحاذاة عند استخدام أداة التحكم الدوّارة من خلال PagerState مشترَك.
  4. AnimatedPage: يتم وضعها داخل دالة lambda الخاصة بمحتوى صفحة أداة التقسيم إلى صفحات. وهي تغلف كل صفحة على حدة لتطبيق رسوم متحركة للانتقال في 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 لتطبيق فيزياء حركة النوابض في Material 3 وتفعيل ميزة المحاذاة التلقائية للصفحات المتجاوبة (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.