Wear OS için Compose ile Sayfalandırma

Sayfalama, kullanıcıların Wear OS cihazlarda farklı tam ekran sayfalar arasında yatay veya dikey olarak kaydırmasına olanak tanır. Yaygın kullanım alanları arasında egzersiz uygulamasında antrenman metrikleri ve medya kontrolleri arasında kaydırma veya çok sayfalı akışlarda ilerleme yer alır.

Wear OS Material 3 için Compose'da HorizontalPagerScaffold ve VerticalPagerScaffold, sayfa düzenini koordine ederek sayfa göstergesini otomatik olarak konumlandırır ve TimeText ile geçişleri yönetir. Her sayfa, yuvarlak ekranda geçiş yaparken ölçeklendirme, yuvarlak köşe dönüştürme ve scrim efektleri uygulayan AnimatedPage composable'ı ile sarmalanır.

Aşağıdaki animasyonda, yatay kaydırma sırasında HorizontalPagerScaffold ve AnimatedPage ile sayfaların nasıl ölçeklendirildiği ve animasyonlandırıldığı gösterilmektedir:

Sayfalama için iskele hiyerarşisi

Material 3'te sayfalandırılmış ekran düzenleri tasarlarken (sola veya sağa (HorizontalPager) ya da yukarı veya aşağı (VerticalPager) kaydırma), dış kapsayıcıdan iç içeriğe doğru aşağıdaki bileşen hiyerarşisini kullanın:

  1. AppScaffold: Uygulamanızın kökündeki en dıştaki kapsayıcıdır. (Uygulama başına yalnızca bir tane kullanın.) Global TimeText yer paylaşımını sabitler. Böylece, sayfa geçişleri ve kaydırarak kapatma hareketleri sırasında saat ekranın üst kısmında sabit kalır.
  2. HorizontalPagerScaffold veya VerticalPagerScaffold: Sayfalayıcı düzeyinde AppScaffold içine yerleştirilir. TimeText ile HorizontalPageIndicator veya VerticalPageIndicator arasındaki geçişleri koordine eder.
  3. HorizontalPager veya VerticalPager: Ortak bir PagerState kullanarak kaydırma hareketlerini, fırlatma fiziğini ve döner girişin yerine oturmasını yöneten temel sayfalayıcı kapsayıcı.
  4. AnimatedPage: Sayfalayıcının sayfa içeriği lambda'sının içine yerleştirilir. Bu işlev, PagerState içindeki sayfanın uzaklığına göre Material 3 geçiş animasyonları (ölçeklendirme ve karartma efektleri) uygulamak için her bir sayfayı sarmalar.
  5. ScreenScaffold: Her bir sayfa için AnimatedPage içine yerleştirilir. Her sayfa kendi dikey olarak kaydırılabilir listesini (ör. TransformingLazyColumn) veya kendi EdgeButton öğesini içerebileceğinden ScreenScaffold öğesini her sayfanın içine yerleştirmek, dikey kaydırma göstergelerinin, kenar düğmelerinin ve contentPadding öğesinin söz konusu sayfaya ait olmasını ve sayfayla birlikte sorunsuz bir şekilde animasyon oluşturmasını sağlar.
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)

Yatay sayfa ayırıcı uygulama

Material 3'te yatay sayfa ayırıcı uygulamak için HorizontalPagerScaffold, HorizontalPager, AnimatedPage ve ScreenScaffold öğelerini uygulamanızın AppScaffold içine yerleştirin.

Materyal 3 yaylı hareket fiziğini ve duyarlı sayfa tutturmayı (HighSnapPositionalThreshold) uygulamak için flingBehavior yerine PagerScaffoldDefaults.snapWithSpringFlingBehavior kullanın. Varsayılan olarak HorizontalPager, döner sayfa kaydırmayı (rotaryScrollableBehavior = null) devre dışı bırakır. Böylece, etkin sayfanın içindeki dikey listeleri (ör. TransformingLazyColumn) kaydırmak için döner ayar düğmesi veya çerçeve girişi kullanılabilir.

Aşağıdaki örnekte, eksiksiz bir HorizontalPagerScaffold kurulumu gösterilmektedir:

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

Yatay sayfalayıcınızdaki bir sayfada TransformingLazyColumn varsa bu sayfanın kapsamı içinde ayrı bir rememberTransformingLazyColumnState() oluşturun ve bunu içteki ScreenScaffold ile TransformingLazyColumn'ya iletin:

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

Dikey sayfa ayırıcı uygulama

Dikey sayfalayıcı, kullanıcıların sayfalar arasında yukarı ve aşağı kaydırmasına veya kol saatinin dönen yan düğmesini ya da çerçevesini kullanarak sayfalar arasında geçiş yapmasına olanak tanır.

HorizontalPager'dan farklı olarak VerticalPager, varsayılan olarak döner sayfa tutturmayı (RotaryScrollableDefaults.snapBehavior(state)) etkinleştirir. PagerScaffoldDefaults.snapWithSpringFlingBehaviorflingBehavior'a iletin ve her sayfayı AnimatedPage ile ScreenScaffold içine alın:

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

Çağrı cihazı davranışını özelleştirme

HorizontalPagerScaffold ve VerticalPagerScaffold öğelerinin tutturma hassasiyetini uygulamanızın etkileşim ihtiyaçlarına göre özelleştirebilirsiniz.

Antrenmanlar için tutturma hassasiyetini ayarlama

Kullanıcının kaba motor kontrolünün sınırlı olduğu antrenmanlar veya yüksek hareketli aktiviteler sırasında, yanlışlıkla dokunmalar veya ayar düğmesinde hafif dönüşler sayfaların istenmeden değiştirilmesine neden olabilir. Bu ekranlar için çağrı cihazınızı düşük tutturma hassasiyetiyle yapılandırın:

  • maxFlingPages = 0 ile PagerDefaults.snapFlingBehavior öğesini geçirin ve snapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThreshold. Bu nedenle, sayfayı çevirmek için ekranın daha büyük bir bölümünde kasıtlı olarak sürükleme yapılması gerekir.
  • Döner sayfa tutturmayı etkinleştirirseniz bir sonraki sayfaya tutturmadan önce daha fazla döndürme gerektirmek için snapSensitivity = RotaryScrollableDefaults.LowSnapSensitivity içinde RotaryScrollableDefaults.snapBehavior değerini ayarlayın.
@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
                    }
                }
            }
        }
    }
}

Tam örnek uygulamalar için android/snippets deposundaki Pager.kt ve AndroidX deposundaki PagerScaffoldSample.kt bölümüne bakın.