Compose 中的分页器
在 Jetpack Compose 中,可以使用 HorizontalPager 和 VerticalPager 可组合项实现水平或垂直翻页功能。它们类似于视图系统中的 ViewPager,默认情况下占据整个屏幕宽度或高度,一次只能滑动一页。
HorizontalPager
创建一个可左右水平滚动的分页器:
// Display 10 items
val pagerState = rememberPagerState(pageCount = { 10 })
HorizontalPager(state = pagerState) { page ->
// Our page content
Text(text = "Page: $page", modifier = Modifier.fillMaxWidth())
}
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 中的页面会按需延迟组合和布局。当用户滚动时,不再需要的页面会被移除。
加载更多屏幕外页面
默认情况下,分页器只加载可见页面。要加载更多屏幕外页面,设置 beyondBoundsPageCount:
HorizontalPager(
state = pagerState,
beyondViewportPageCount = 2 // 加载两侧各2个额外页面
) { page ->
// page content
}
滚动到特定页面
使用 PagerState 滚动到特定页面:
val pagerState = rememberPagerState(pageCount = { 10 })
val coroutineScope = rememberCoroutineScope()
HorizontalPager(state = pagerState) { page ->
Text(text = "Page: $page", modifier = Modifier.fillMaxWidth().height(100.dp))
}
Button(
onClick = {
coroutineScope.launch {
// 滚动到第5页(无动画)
pagerState.scrollToPage(5)
}
},
modifier = Modifier.align(Alignment.BottomCenter)
) {
Text("Jump to Page 5")
}
添加动画效果:
Button(
onClick = {
coroutineScope.launch {
// 滚动到第5页(带动画)
pagerState.animateScrollToPage(5)
}
},
modifier = Modifier.align(Alignment.BottomCenter)
) {
Text("Jump to Page 5")
}
监听页面状态变化
PagerState 有三个重要属性:
currentPage:最接近贴靠位置的页面settledPage:未运行动画或滚动时的页面targetPage:滚动动作的建议停止位置
监听页面变化:
val pagerState = rememberPagerState(pageCount = { 10 })
LaunchedEffect(pagerState) {
// 监听 currentPage 变化
snapshotFlow { pagerState.currentPage }.collect { page ->
// 处理页面变化,例如发送分析事件
Log.d("Page change", "Page changed to $page")
}
}
VerticalPager(state = pagerState) { page ->
Text(text = "Page: $page")
}
添加页面指示器
创建圆形页面指示器:
val pagerState = rememberPagerState(pageCount = { 4 })
HorizontalPager(state = pagerState, modifier = Modifier.fillMaxSize()) { page ->
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)
)
}
}
为内容应用滚动效果
根据滚动位置应用效果,如调整不透明度:
val pagerState = rememberPagerState(pageCount = { 4 })
HorizontalPager(state = pagerState) { page ->
Card(
Modifier.size(200.dp).graphicsLayer {
// 计算当前页面与所选页面的距离
val pageOffset = ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue
// 根据距离调整不透明度(50%-100%)
alpha = lerp(start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f))
}
) {
// Card content
}
}
自定义页面尺寸
默认情况下,分页器占据全部宽度/高度。可以自定义页面大小:
// 设置固定宽度
val pagerState = rememberPagerState(pageCount = { 4 })
HorizontalPager(
state = pagerState,
pageSize = PageSize.Fixed(100.dp) // 每页100dp宽
) { page ->
// page content
}
根据视口大小自定义计算:
private val threePagesPerViewport = object : PageSize {
override fun Density.calculateMainAxisPageSize(
availableSpace: Int,
pageSpacing: Int
): Int {
return (availableSpace - 2 * pageSpacing) / 3
}
}
HorizontalPager(
state = pagerState,
pageSize = threePagesPerViewport
) { page ->
// page content
}
内容内边距
设置内容内边距来控制页面对齐方式:
// 起始内边距 - 页面向末尾对齐
HorizontalPager(
state = pagerState,
contentPadding = PaddingValues(start = 64.dp)
) { page ->
// page content
}
// 两侧内边距 - 页面水平居中
HorizontalPager(
state = pagerState,
contentPadding = PaddingValues(horizontal = 32.dp)
) { page ->
// page content
}
// 末端内边距 - 页面向开头对齐
HorizontalPager(
state = pagerState,
contentPadding = PaddingValues(end = 64.dp)
) { page ->
// page content
}
自定义滚动行为
调整贴靠距离,控制一次滑动能滚动的最大页面数:
val pagerState = rememberPagerState(pageCount = { 10 })
val fling = PagerDefaults.flingBehavior(
state = pagerState,
pagerSnapDistance = PagerSnapDistance.atMost(10) // 一次最多滚动10页
)
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
state = pagerState,
pageSize = PageSize.Fixed(200.dp),
beyondViewportPageCount = 10,
flingBehavior = fling
) {
// page content
}
}
创建自动翻页器
实现一个自动前进的分页器,用户交互时暂停:
@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()
// 当pager被拖动或页面被按压时停止自动前进
val autoAdvance = !pagerIsDragged && !pageIsPressed
if (autoAdvance) {
LaunchedEffect(pagerState, pageInteractionSource) {
while (true) {
delay(2000) // 每2秒切换一次
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
) {
// 处理页面点击
}
.wrapContentSize(align = Alignment.Center)
)
}
PagerIndicator(pageItems.size, pagerState.currentPage)
}
}
@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)
)
}
}
}
}
通过这些功能,Compose 分页器提供了灵活且强大的页面导航体验,适用于各种应用场景。