类似于 ViewPager,1.4 版本之前需要借助 accompanis 库,底层基于 LazyColumn、LazyRow 实现,在使用上也基本相同。默认情况下 HorizontalPager 占据屏幕的整个宽度,VerticalPager 会占据整个高度。
fun HorizontalPager(
pageCount: Int, //页面数量
modifier: Modifier = Modifier,
state: PagerState = rememberPagerState(), //控制监听页面状态的对象
contentPadding: PaddingValues = PaddingValues(0.dp), //内容内边距
pageSize: PageSize = PageSize.Fill, //页面填充模式(填充满Fill,自适应Fixed)
beyondBoundsPageCount: Int = 0, //当前页面前后预加载的页面数量
pageSpacing: Dp = 0.dp, //两个页面之间的间隙
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state), //用于滚动后手势的flingBehavior
userScrollEnabled: Boolean = true, //是否允许通过用户手势或辅助功能进行滚动(即使禁用PagerState.scroll,您仍然可以使用它以编程方式滚动)
reverseLayout: Boolean = false, //反转页面顺序
key: ((index: Int) -> Any)? = null, //表示项目的稳定且唯一的密钥。当您指定键时,滚动位置将根据键保持,这意味着如果您在当前可见项目之前添加/删除项目,则具有给定键的项目将保留为第一个可见项目。
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Horizontal
), //一个嵌套的ScrollConnection,用于指示此Pager如何使用嵌套列表。默认行为将使Pager消耗所有嵌套的delta。
pageContent: @Composable (page: Int) -> Unit
)
HorizontalPager(
pageCount = 10,
modifier = Modifier.size(100.dp)
) { page ->
// 每一页的内容,比如显示个文本
Text(
text = "Page: $page",
modifier = Modifier.fillMaxSize()
)
}
将 beyondBoundsPageCount 属性设为 >0 的整数,会在当前页面左右各加载相同数量的页面。
使用 rememberPagerState() 创建一个 PagerState 对象,并将其作为 state 参数传递给分页器。在 CoroutineScope 中对此状态调用 PagerState.scrollToPage(),带动画跳转使用 PagerState.animateScrollToPage()。
val pagerState = rememberPagerState( 10 ) //10是页面数量
HorizontalPager(
state = pagerState,
modifier = Modifier.size(100.dp)
) { page ->
Text(
text = "Page: $page",
modifier = Modifier.fillMaxSize()
)
}
val coroutineScope = rememberCoroutineScope()
Button(
modifier = Modifier.align(Alignment.BottomCenter),
onClick = {
coroutineScope.launch {
pagerState.scrollToPage(5)
// pagerState.animateScrollToPage(5) //带动画跳转
}
}
) {
Text("跳到页面5")
}
通过 pagerState.pageCount 获取页面数量,并绘制自定义指示器。使用 pagerState.currentPage 获取当前显示页面的索引,改变对应指示器的颜色。
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(bottom = 2.dp),
horizontalArrangement = Arrangement.Center
) {
repeat(pagerState.pageCount) { index ->
val color = if (pagerState.currentPage == index) Colors.black else Colors.gray
Box(modifier = Modifier
.padding(2.dp)
.clip(CircleShape)
.background(color)
.size(10.dp)
)
}
}
@Composable
fun Demo() {
val tabList = listOf("最新","广场","问答","项目")
val pagerState = rememberPagerState { tabList.size }
val coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier.fillMaxSize()
) {
TabRow(
modifier = Modifier
.padding(vertical = 10.dp)
.fillMaxWidth()
.height(20.dp),
selectedTabIndex = pagerState.currentPage,
containerColor = AppColors.transparent,
indicator = {},
divider = {}
) {
tabList.forEachIndexed { index, title ->
Tab(
text = { Text(
text = title,
fontSize = if (pagerState.currentPage == index) 15.sp else 15.sp,
fontWeight = if (pagerState.currentPage == index) FontWeight.ExtraBold else FontWeight.Bold) },
selected = pagerState.currentPage == index,
selectedContentColor = AppTheme.colors.textPrimary,
unselectedContentColor = AppTheme.colors.textSecondary,
onClick = { coroutineScope.launch { pagerState.scrollToPage(index) } }
)
}
}
HorizontalPager(
state = pagerState,
beyondBoundsPageCount = 1,
pageSpacing = Dimension.contentMargin
) { index ->
when (index) {
0 -> { NewestPage() }
1 -> { SquarePage() }
2 -> { QaPage() }
3 -> { ProjectPage() }
}
}
}
}