用Compose实现一个Banner轮播组件

在Android View体系中,我们想实现Banner轮播效果的话,可以使用ViewPager2来实现,ViewPager2基于RecyclerView来实现滑动效果,支持横向和纵向,非常方便。在Compose中要想实现一样的横竖Banner轮播效果,可以使用HorizontalPager和VerticalPager两个组件来实现。

HorizontalPager实现轮播切换

HorizontalPager的使用非常简单,构造一个pagerState,再根据自己的需求,创建每一页的视图,就可以达到ViewPager的效果了,相比于ViewPager设置一个Adapter,要简单很多。示例如下

kotlin 复制代码
@Composable
fun <T> banner() {
  val images = listOf(R.drawable.imgA,R.drawable.imgB,R.drawable.imgC)
  val pagerState = rememberPagerState(pageCount = { images.size })
  HorizontalPager(pagerState, modifier = Modifier.fillMaxSize()) {
    Image(
      painter = painterResource(images[it]),
      contentDescription = null,
      modifier = Modifier.fillMaxSize()
    )
  }
}

一个简单的左右滑动切换就实现了,若想实现上下滑动切换,则只需将HorizontalPager换成VerticalPager就可以了。上面的代码以加载本地Drawable目录下的3张图片为例实现切换效果,若想加载网络图片,借助三方库,将Image或者painter替换下就可以了。默认情况下,HorizontalPager仅加载屏幕上可见的页面,如需加载更多屏幕外的网页,将beyondViewportPageCount设置为大于0的值就可以了。

实现Banner无限滑动切换

HorizontalPager并不支持无限切换效果,要想实现无限轮播,只能自己实现相关逻辑。以上面的三张图片A、B和C为例,无限轮播需要我们将图片C向右继续切换到图片A,图片A向左继续切换到图片C,以达到循环的效果,效果如下

如上图,切换到最后一张A图片时,我们需要手动将当前位置设置为1,这样显示图片A时才能够继续左右滑动。同理当切换到最左端的图片C时,需要手动将当前位置设置为3,这样显示图片C时才能够继续左右滑动。

为了不影响数据源,我们需要将HorizontalPager的大小设置的比数据大2,当位置处于0时,取数据的最后一个,当位置处于最后一个时,取数据第一个。示例如下

kotlin 复制代码
@Composable
fun <T> banner() {
  val images = listOf(R.drawable.imgA,R.drawable.imgB,R.drawable.imgC)
  val pagerState = rememberPagerState(1, pageCount = { images.size + 2 })
  LaunchedEffect(pagerState.currentPage) {
    if (pagerState.currentPage == 0) {
      pagerState.scrollToPage(pagerState.pageCount - 2)
    } else if (pagerState.currentPage == pagerState.pageCount - 1) {
      pagerState.scrollToPage(1)
    }
  }
  HorizontalPager(pagerState, modifier = Modifier.fillMaxSize()) { index ->
    val imageIndex = when (index) {
      0 -> pagerState.pageCount - 3
      pagerState.pageCount - 1 -> 0
      else -> index - 1
    }
    Image(
      painter = painterResource(images[imageIndex]),
      contentDescription = null,
      modifier = Modifier.fillMaxSize()
    )
  }
}

优化Banner切换无过度动画效果

当我们使用LaunchedEffect来监听pagerState.currentPage的改变,并手动重置位置时会出现一个问题,由位置3的图片向右滑动切换到图片A,或者由位置1的图片A向左滑动切换到图片C时,由于我们手动重置了位置,没有加动画效果,导致屏幕立即刷新了,效果非常不好。这是因为currentPage在足够接近贴靠位置时,currentPage会立即更新。而settledPage会在动画运行完毕后进行更新,所以只需用settledPage就能解决此问题,示例如下

kotlin 复制代码
LaunchedEffect(pagerState.settledPage) {
    if (pagerState.currentPage == 0) {
      pagerState.scrollToPage(pagerState.pageCount - 2)
    } else if (pagerState.currentPage == pagerState.pageCount - 1) {
      pagerState.scrollToPage(1)
    }
  }

设置Banner自动轮播

前面我们实现了Banner无限轮播,要实现自动轮播,只需要加个定时器,让它自动切换下一页就行了,示例如下

kotlin 复制代码
@Composable
fun <T> banner() {
  LaunchedEffect(Unit) {
    while (true) {
      delay(3000L)
      pagerState.animateScrollToPage(pagerState.currentPage + 1)
    }
  }
}

给Banner添加指示器

需要注意的是指示器的数量是与数据源一致,需要将pagerState的索引下标转换成和数据源索引一致,即第1个位置对接数据源和指示器最后一个,最后一个位置对接数据源和指示器第1个位置,其它位置减1对接数据源和指示器实际位置,因为在最前面插入了最后一个视频,所以要减1.指示器示例如下

kotlin 复制代码
@Composable
fun <T> banner() {
  val images = listOf(R.drawable.imgA,R.drawable.imgB,R.drawable.imgC)
  val pagerState = rememberPagerState(pageCount = { images.size + 2 })
  Row(
    Modifier
      .wrapContentSize()
      .align(Alignment.BottomCenter)
      .padding(8.dp),
    horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
  ) {
    repeat(images.size) {
      val imageIndex = when (pagerState.currentPage) {
        0 -> pagerState.pageCount - 3
        pagerState.pageCount - 1 -> 0
        else -> pagerState.currentPage - 1
      }
      Box(
        modifier = Modifier
          .size(DpSize(8.dp, 8.dp))
          .clip(CircleShape)
          .background(if (imageIndex == it) Color.Red else Color.LightGray)
      )
    }
  }
}
相关推荐
用户69371750013842 小时前
Kotlin 协程 快速入门
android·后端·kotlin
狂团商城小师妹2 小时前
JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
android·java·ios
老华带你飞3 小时前
记录生活系统|记录美好|健康管理|基于java+Android+微信小程序的记录生活系统设计与实现(源码+数据库+文档)
android·java·数据库·vue.js·生活·毕设·记录生活系统
峥嵘life3 小时前
Android16 更新fastboot版本解决fastbootd模式识别不到设备问题
android·学习
puyaCheer4 小时前
Android 打开 在线 pdf 文件
android·pdf
RainbowC013 小时前
从Dalvik字节码角度优化安卓编码
android·java/jvm
河铃旅鹿14 小时前
Android开发-java版:布局
android·笔记·学习
Meteors.15 小时前
安卓进阶——RxJava
android·rxjava
drsonxu17 小时前
Android开发自学笔记 --- 构建简单的UI视图
android·compose