Penomoran dengan Compose untuk Wear OS

Penomoran memungkinkan pengguna menggeser secara horizontal atau vertikal di antara halaman layar penuh yang berbeda di perangkat Wear OS. Kasus penggunaan umum mencakup menggeser antara metrik latihan fisik dan kontrol media di aplikasi latihan fisik, atau menelusuri alur multi-halaman.

Di Compose untuk Wear OS Material 3, HorizontalPagerScaffold dan VerticalPagerScaffold mengoordinasikan tata letak penomoran halaman, secara otomatis memosisikan indikator halaman dan mengelola transisi dengan TimeText. Setiap halaman di-wrap dalam composable AnimatedPage, yang menerapkan penskalaan, pengubahan bentuk sudut bulat, dan efek scrim saat halaman bertransisi di layar bulat.

Animasi berikut menunjukkan cara HorizontalPagerScaffold dan AnimatedPage menskalakan dan menganimasikan halaman selama geser horizontal:

Hierarki scaffold untuk penomoran halaman

Saat mendesain tata letak layar yang dipaginasi di Material 3—baik menggeser ke kiri atau kanan (HorizontalPager) maupun ke atas atau bawah (VerticalPager)—gunakan hierarki komponen berikut dari penampung luar ke konten dalam:

  1. AppScaffold: Penampung terluar di root aplikasi Anda (hanya gunakan satu per aplikasi). Hal ini menyematkan overlay TimeText global sehingga jam tetap berada di bagian atas layar selama transisi halaman dan gestur geser untuk menutup.
  2. HorizontalPagerScaffold atau VerticalPagerScaffold: Ditempatkan di dalam AppScaffold di tingkat pager. Fungsi ini mengoordinasikan transisi antara TimeText dan HorizontalPageIndicator atau VerticalPageIndicator.
  3. HorizontalPager atau VerticalPager: Penampung pager dasar yang mengelola gestur geser, fisika lemparan, dan penyesuaian input putar menggunakan PagerState bersama.
  4. AnimatedPage: Ditempatkan di dalam lambda konten halaman pager. Komponen ini membungkus setiap halaman individual untuk menerapkan animasi transisi Material 3 (efek penskalaan dan scrim) berdasarkan offset halaman di PagerState.
  5. ScreenScaffold: Ditempatkan di dalam AnimatedPage untuk setiap halaman individual. Karena setiap halaman dapat berisi daftar yang dapat di-scroll secara vertikal (seperti TransformingLazyColumn) atau EdgeButton-nya sendiri, menyusun ScreenScaffold di dalam setiap halaman memastikan bahwa indikator scroll vertikal, tombol tepi, dan contentPadding termasuk dalam halaman tertentu tersebut dan beranimasi dengan lancar.
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)

Mengimplementasikan pager horizontal

Untuk menerapkan pager horizontal di Material 3, susun HorizontalPagerScaffold, HorizontalPager, AnimatedPage, dan ScreenScaffold di dalam AppScaffold aplikasi Anda.

Gunakan PagerScaffoldDefaults.snapWithSpringFlingBehavior untuk flingBehavior guna menerapkan fisika gerakan pegas Material 3 dan penyesuaian halaman responsif (HighSnapPositionalThreshold). Secara default, HorizontalPager menonaktifkan scroll halaman putar (rotaryScrollableBehavior = null), sehingga input putar kenop jam atau bingkai dapat men-scroll daftar vertikal (seperti TransformingLazyColumn) di dalam halaman aktif.

Contoh berikut menunjukkan penyiapan HorizontalPagerScaffold lengkap:

@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") }
                            }
                        }
                    }
                }
            }
        }
    }
}

Jika halaman di dalam pager horizontal Anda berisi TransformingLazyColumn, buat rememberTransformingLazyColumnState() terpisah di dalam cakupan halaman tersebut dan teruskan ke ScreenScaffold dan TransformingLazyColumn bagian dalam:

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")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Menerapkan pager vertikal

Pager vertikal memungkinkan pengguna menggeser ke atas dan ke bawah antarhalaman atau menelusurinya menggunakan tombol samping atau bezel berputar smartwatch.

Tidak seperti HorizontalPager, VerticalPager memungkinkan penyesuaian halaman rotari secara default (RotaryScrollableDefaults.snapBehavior(state)). Teruskan PagerScaffoldDefaults.snapWithSpringFlingBehavior ke flingBehavior dan bungkus setiap halaman dalam AnimatedPage dan 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")
                        }
                    }
                }
            }
        }
    }
}

Menyesuaikan perilaku penomoran halaman

Anda dapat menyesuaikan sensitivitas penarikan HorizontalPagerScaffold dan VerticalPagerScaffold agar sesuai dengan kebutuhan interaksi aplikasi Anda.

Menyesuaikan sensitivitas penarikan untuk latihan fisik

Selama latihan fisik atau aktivitas dengan gerakan tinggi yang membatasi kontrol motorik kasar pengguna, sentuhan yang tidak disengaja atau sedikit putaran kenop dapat secara tidak sengaja mengganti halaman. Untuk layar ini, konfigurasi pager Anda dengan sensitivitas penarikan rendah:

  • Lewati PagerDefaults.snapFlingBehavior dengan maxFlingPages = 0 dan snapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThreshold sehingga penarikan yang disengaja di sebagian besar layar diperlukan untuk membuka halaman.
  • Jika Anda mengaktifkan penyelarasan halaman putar, tetapkan snapSensitivity = RotaryScrollableDefaults.LowSnapSensitivity di RotaryScrollableDefaults.snapBehavior untuk memerlukan lebih banyak rotasi sebelum menyelaraskan ke halaman berikutnya.
@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
                    }
                }
            }
        }
    }
}

Untuk implementasi contoh lengkap, lihat Pager.kt di repositori android/snippets dan PagerScaffoldSample.kt di repositori AndroidX.