分页功能可让用户在 Wear OS 设备上以水平或垂直方式在不同的全屏页面之间滑动。常见用例包括在锻炼应用中滑动切换锻炼指标和媒体控件,或逐步完成多页面流程。
在适用于 Wear OS 的 Compose Material 3 中,HorizontalPagerScaffold 和 VerticalPagerScaffold 可协调分页器布局,自动定位页面指示器并使用 TimeText 管理过渡效果。每个页面都封装在 AnimatedPage 可组合项中,该可组合项会在页面在圆形显示屏上切换时应用缩放、圆角变形和幕帘效果。
以下动画展示了 HorizontalPagerScaffold 和 AnimatedPage 在水平滑动期间如何缩放和动画显示页面:
分页的支架层次结构
在 Material 3 中设计分页屏幕布局时,无论是向左或向右滑动 (HorizontalPager) 还是向上或向下滑动 (VerticalPager),都应使用以下组件层次结构(从外部容器到内部内容):
AppScaffold:应用根目录中的最外层容器(每个应用只能使用一个)。它会锚定全局TimeText叠加层,以便在页面转换和滑动关闭手势期间,时钟保持静止在屏幕顶部。HorizontalPagerScaffold或VerticalPagerScaffold:放置在分页器级别的AppScaffold内。它协调TimeText与HorizontalPageIndicator或VerticalPageIndicator之间的转换。HorizontalPager或VerticalPager:基础分页容器,使用共享的PagerState管理滑动操作、快速滑动物理特性和旋转输入贴靠。AnimatedPage:放置在分页器的页面内容 lambda 中。它会封装每个单独的页面,以根据页面在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)
实现横向页面控制项
如需在 Material 3 中实现水平分页器,请将 HorizontalPagerScaffold、HorizontalPager、AnimatedPage 和 ScreenScaffold 嵌套在应用的 AppScaffold 中。
使用 flingBehavior 的 PagerScaffoldDefaults.snapWithSpringFlingBehavior 可应用 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,因此需要有意地在屏幕的较大部分区域进行拖动才能翻页。 - 如果您启用旋转页面贴靠,请在
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。