Jetpack Compose目前没有官方的Banner控件,所以只能自己写,搜了些资料才完成,非常感谢之前分享过这些内容的大佬们。
效果图
accompanist组库
旨在为Jetpack Compose提供补充功能的组库,里面有非常多很好用的实验性功能,之前用过的加载网络图片的rememberImagePainter就是其中之一,而做Banner的话需要用到的是其中的Pager库。
//导入依赖
implementation "com.google.accompanist:accompanist-pager:$accompanist_pager"
这里我用的是0.16.1,因为其他库也是这个版本,目前最新是0.18.0
关键代码
1、rememberPagerState
用于记录分页状态的变量,一共有5个参数,我们用到了4个,还有一个是initialPageOffset,可以设置偏移量
val pagerState = rememberPagerState(
//总页数
pageCount = list.size,
//预加载的个数
initialOffscreenLimit = 1,
//是否无限循环
infiniteLoop = true,
//初始页面
initialPage = 0
)
2、HorizontalPager
用于创建一个可以横向滑动的分页布局,把上面的rememberPagerState传进去,其他也没啥
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxSize(),
) {
page ->
Image(
painter = rememberImagePainter(list[page].imageUrl),
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
contentDescription = null
)
}
3、让HorizontalPager自己动起来
这里有两个方法可以让HorizontalPager动起来,一个是animateScrollToPage***,另一个是scrollToPage*,从名字上都可以看出来带animate的是有动画效果的方法,也正是我想要的东西。
//自动滚动
LaunchedEffect(pagerState.currentPage) {
if (pagerState.pageCount > 0) {
delay(timeMillis)
pagerState.animateScrollToPage((pagerState.currentPage + 1) % pagerState.pageCount)
}
}
在控件里添加这行代码就可以让控件自动起来了
但这是一段看起来没问题的代码
假设页面总数pagerState.pageCount为2,当((pagerState.currentPage + 1) % pagerState.pageCount) == 0时跳转到第1个页面,但最后的效果是这样的
轮播图往左滑了,而且还出现了轮播图中间页面的画面,页面有点闪烁的感觉。
修改后
//自动滚动
LaunchedEffect(pagerState.currentPage) {
if (pagerState.pageCount > 0) {
delay(timeMillis)
//这里直接+1就可以循环,前提是pagerState的infiniteLoop == true
pagerState.animateScrollToPage(pagerState.currentPage + 1)
}
}
只修改了animateScrollToPage参数的值,看到这里可能有人会问:pagerState.currentPage + 1不会报错吗?
确实不会!
因为当rememberPagerState中的infiniteLoop(无限循环)参数设置为true时最大页码其实为Int.MAX_VALUE,而currentPage只是当前页面的索引,并不是真实的页码。
也就是说,当Banner有4个页面,这里传个5的时候,并不会报错,而且animateScrollToPage会自动将这个"5"转换为页面索引,以保证下次使用currentPage不会出错。(菜鸟,我!啊吧啊吧看了好一阵子源码没看到这个是哪里转的)
不过有些地方值得注意:
调用pagerState.animateScrollToPage(target)的时候
- 当target > pageCount 或 target > currentPage的时候,控件向右滑动
- 当target < pageCount 且 target < currentPage的时候,控件向左滑动
- 另外如果currentPage和target当两者相差页面大于4的时候只会在动画中显示(currentPage、currentPage + 1、target - 1、target)四个页面
以此类推,如果改为-1的话就是不断往左自动滑动啦
pagerState.animateScrollToPage(pagerState.currentPage - 1)
Banner中定义了几个参数,indicatorAlignment可以设置指示点的位置,默认为底部居中
/**
* 轮播图
* [timeMillis] 停留时间
* [loadImage] 加载中显示的布局
* [indicatorAlignment] 指示点的的位置,默认是轮播图下方的中间,带一点padding
* [onClick] 轮播图点击事件
*/
@ExperimentalCoilApi
@ExperimentalPagerApi
@Composable
fun Banner(
list: List<BannerData>?,
timeMillis: Long = 3000,
@DrawableRes loadImage: Int = R.mipmap.ic_web,
indicatorAlignment: Alignment