ページングを使用すると、Wear OS デバイスで全画面表示の個別のページ間を水平または垂直にスワイプできます。一般的なユースケースとしては、エクササイズ アプリでワークアウト指標とメディア コントロールの間をスワイプしたり、複数ページのフローをステップ実行したりする場合などがあります。
Compose for Wear OS Material 3 では、HorizontalPagerScaffold と VerticalPagerScaffold がページャー レイアウトを調整し、ページ インジケーターを自動的に配置して、TimeText でトランジションを管理します。各ページは AnimatedPage コンポーザブルでラップされ、丸いディスプレイでページが切り替わる際に、スケーリング、角の丸み、スクリム効果が適用されます。
次のアニメーションは、水平スワイプ中に HorizontalPagerScaffold と AnimatedPage がページをスケーリングしてアニメーション化する方法を示しています。
ページングの Scaffold 階層
Material 3 でページネーションされた画面レイアウトを設計する際は、左右(HorizontalPager)または上下(VerticalPager)のスワイプにかかわらず、外側のコンテナから内側のコンテンツまで、次のコンポーネント階層を使用します。
AppScaffold: アプリのルートにある最も外側のコンテナ(アプリごとに 1 つのみ使用)。グローバルTimeTextオーバーレイを固定し、ページ遷移やスワイプして閉じるジェスチャーの際に、時計が画面上部に固定されたままになるようにします。HorizontalPagerScaffoldまたはVerticalPagerScaffold: ページャレベルでAppScaffoldの内部に配置されます。TimeTextとHorizontalPageIndicatorまたはVerticalPageIndicatorの間の切り替えを調整します。HorizontalPagerまたはVerticalPager: 共有のPagerStateを使用して、スワイプ操作、フリングの物理演算、ロータリー入力のスナップを管理する基盤となるページャー コンテナ。AnimatedPage: ページャのページ コンテンツ ラムダ内に配置されます。各ページをラップして、PagerStateのページのオフセットに基づいて Material 3 のトランジション アニメーション(スケーリングとスクリム効果)を適用します。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)
横方向ページャを実装する
マテリアル 3 で水平ページャーを実装するには、アプリの AppScaffold 内に HorizontalPagerScaffold、HorizontalPager、AnimatedPage、ScreenScaffold をネストします。
flingBehavior に PagerScaffoldDefaults.snapWithSpringFlingBehavior を使用すると、マテリアル 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 のスナップ感度をカスタマイズできます。
ワークアウトのスナップ感度を調整する
ワークアウトや、ユーザーの粗大運動制御が制限される動きの多いアクティビティでは、誤って画面に触れたり、リューズを少し回転させたりすると、意図せずにページが切り替わることがあります。これらの画面では、ページャの感度を低く設定します。
maxFlingPages = 0とsnapPositionalThreshold = PagerScaffoldDefaults.LowSnapPositionalThresholdでPagerDefaults.snapFlingBehaviorを渡します。そのため、ページをめくるには、画面の大部分を意図的にドラッグする必要があります。- ロータリー ページ スナップを有効にする場合は、
RotaryScrollableDefaults.snapBehaviorのsnapSensitivity = RotaryScrollableDefaults.LowSnapSensitivityを設定して、次のページにスナップする前に回転を増やすようにします。
@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 } } } } } }
完全なサンプル実装については、android/snippets リポジトリの Pager.kt と AndroidX リポジトリの PagerScaffoldSample.kt をご覧ください。