要以左右或上下方式翻阅内容,您可以使用 HorizontalPager
和 VerticalPager
可组合项。这些可组合项与视图系统中的 ViewPager
具有相似的功能。默认情况下,HorizontalPager
占据屏幕的整个宽度,VerticalPager
占据整个高度,并且 Pager 每次仅可滑动一页。这些默认设置都是可配置的。
HorizontalPager
要创建可左右水平滚动的 Pager,请使用 HorizontalPager
HorizontalPager
演示
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
要创建可上下滚动的 Pager,请使用 VerticalPager
VerticalPager
演示
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
延迟创建
HorizontalPager
和 VerticalPager
中的页面在需要时会延迟组合和布局。当用户滚动页面时,可组合项会移除所有不再需要的页面。
加载更多屏幕外页面
默认情况下,Pager 仅加载屏幕上可见的页面。要加载更多屏幕外页面,请将 beyondBoundsPageCount
设置为大于零的值。
滚动到 Pager 中的某个项目
要滚动到 Pager 中的特定页面,请使用 rememberPagerState()
创建一个 PagerState
对象,并将其作为 state
参数传递给 Pager。您可以在 CoroutineScope
内对此状态调用 PagerState#scrollToPage()
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
如果您想以动画方式滚动到该页面,请使用 PagerState#animateScrollToPage()
函数
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
获取页面状态更改通知
PagerState
具有三个属性,其中包含页面信息:currentPage
、settledPage
和 targetPage
。
currentPage
:最接近吸附位置的页面。默认情况下,吸附位置位于布局的起始处。settledPage
:没有动画或滚动正在运行时所在的页面编号。这与currentPage
属性不同,currentPage
会在页面足够接近吸附位置时立即更新,而settledPage
则会保持不变,直到所有动画运行完毕。targetPage
:滚动移动的建议停止位置。
您可以使用 snapshotFlow
函数来观察这些变量的变化并对其作出反应。例如,要在每次页面更改时发送分析事件,您可以执行以下操作
val pagerState = rememberPagerState(pageCount = { 10 }) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> // Do something with each page change, for example: // viewModel.sendPageSelectedEvent(page) Log.d("Page change", "Page changed to $page") } } VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
添加页面指示器
要向页面添加指示器,请使用 PagerState
对象获取有关从多个页面中选择的页面信息,然后绘制您的自定义指示器。
例如,如果您想要一个简单的圆形指示器,您可以使用 pagerState.currentPage
重复绘制多个圆形,并根据页面是否选中来更改圆形的颜色。
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> // Our page content Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }

将项目滚动效果应用于内容
一种常见的用例是使用滚动位置将效果应用于 Pager 项。要了解页面与当前选定页面的距离,您可以使用 PagerState.currentPageOffsetFraction
。然后,您可以根据与选定页面的距离,将转换效果应用于您的内容。
例如,要根据项目与中心的距离调整其不透明度,请使用 Pager 中项目的 Modifier.graphicsLayer
更改 alpha
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager(state = pagerState) { page -> Card( Modifier .size(200.dp) .graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions val pageOffset = ( (pagerState.currentPage - page) + pagerState .currentPageOffsetFraction ).absoluteValue // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
自定义页面大小
默认情况下,HorizontalPager
和 VerticalPager
分别占据整个宽度或整个高度。您可以将 pageSize
变量设置为 Fixed
、Fill
(默认)或自定义尺寸计算。
例如,要设置一个固定宽度为 100.dp
的页面
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
要根据视口大小调整页面大小,请使用自定义页面大小计算。创建一个自定义的 PageSize
对象,然后将 availableSpace
除以三,同时考虑到项目之间的间距
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
内容内边距
HorizontalPager
和 VerticalPager
都支持更改内容内边距,这让您可以影响页面的最大尺寸和对齐方式。
例如,设置 start
内边距会将页面向末尾对齐
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
将 start
和 end
内边距都设置为相同的值会使项目水平居中
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
设置 end
内边距会将页面向起始对齐
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
您可以设置 top
和 bottom
值,以实现 VerticalPager
的类似效果。这里仅以 32.dp
值作为示例;您可以将每个内边距尺寸设置为任何值。
自定义滚动行为
默认的 HorizontalPager
和 VerticalPager
可组合项指定了滚动手势如何与 Pager 配合使用。但是,您可以自定义和更改默认设置,例如 pagerSnapDistance
或 flingBehavior
。
吸附距离
默认情况下,HorizontalPager
和 VerticalPager
会将快速滑动可滚动的最大页面数设置为每次一页。要更改此设置,请在 flingBehavior
上设置 pagerSnapDistance
val pagerState = rememberPagerState(pageCount = { 10 }) val fling = PagerDefaults.flingBehavior( state = pagerState, pagerSnapDistance = PagerSnapDistance.atMost(10) ) Column(modifier = Modifier.fillMaxSize()) { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
创建自动前进的 Pager
本部分介绍了如何在 Compose 中创建带有页面指示器的自动前进 Pager。项目集合会自动水平滚动,但用户也可以手动在项目之间滑动。如果用户与 Pager 互动,它将停止自动前进。
基本示例
以下代码段共同创建了一个带有视觉指示器的基本自动前进 Pager 实现,其中每个页面都呈现为不同的颜色
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
代码要点
- The
AutoAdvancePager
function creates a horizontally paging view with automatic advancement. It takes a list ofColor
objects as input, which are used as background colors for each page. pagerState
是使用rememberPagerState
创建的,它保存了 Pager 的状态。pagerIsDragged
和pageIsPressed
用于跟踪用户互动。- 除非用户拖动 Pager 或按下其中一个页面,否则
LaunchedEffect
会每两秒自动前进 Pager。 HorizontalPager
显示一个页面列表,每个页面都有一个Text
可组合项,用于显示页码。该修饰符会填充页面,从pageItems
设置背景颜色,并使页面可点击。
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
代码要点
- 一个
Box
可组合项用作根元素。- 在
Box
内部,一个Row
可组合项水平排列页面指示器。
- 在
- 自定义页面指示器显示为一排圆形,其中每个裁剪为
circle
的Box
代表一个页面。 - 当前页面的圆形显示为
DarkGray
,而其他圆形显示为LightGray
。currentPageIndex
参数决定哪个圆形渲染为深灰色。
结果
此视频显示了前面代码段中的基本自动前进 Pager