• Android Jetpack Compose实现轮播图效果


    Android Jetpack Compose实现轮播图效果

    轮播图效果

    在最近思索如何使用Compose方式改进我的开源TMDB电影列表应用程序的主屏幕时,一个激动人心的概念浮现在我的脑海中——为什么不整合一个吸引人的轮播图来展示即将上映的电影呢?在本文中,我将分享我的开发和实现自定义轮播图的经历,提供涉及不同步骤的见解。

    首先,我搜索了现有的解决方案,考虑到Material Design 3文档中有一个轮播图组件的可用性。然而,我很快发现它尚未发布用于Jetpack Compose,而且仅在最新的MDC-Android(视图系统)Alpha版本中可访问。这迫使我决定开发自己的轮播图,利用HorizontalPager,这是一个Compose组件,允许水平滚动和页面变换。

    为了有效地解决开发过程,我将问题分为四个不同的部分:

    1. HorizontalPager转换为实现所需的旋转木马效果。
    2. 加入指示器显示当前可见页面。
    3. HorizontalPager页面实现自动滚动。
    4. 利用动画增强即将出现的页面的外观,同时聚焦当前显示的页面并淡化其他页面。
      让我们逐个详细讨论这些部分。

    HorizontalPager转换为实现所需的旋转木马效果

    为了创建所需的旋转木马效果,我们必须首先自定义HorizontalPager以部分显示前一个和后一个页面。
    为此,HorizontalPager提供了两个参数:

    1. contentPadding:允许我们从每个轴应用边距,创建所需的页面周围间距。
    2. pageSpacing:在两个相邻页面之间引入间隙,确保视觉上吸引人的布局。

    轮播图效果

    HorizontalPager(
        contentPadding = PaddingValues(horizontal = 32.dp),
        pageSpacing = 16.dp
    ) { page ->
        ...
        CarouselItem(item)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将指示器合并到页面中以显示当前可见页面

    这个任务很容易完成。我只需要将HorizontalPager置于Box布局中,并将DotIndicators合成到Box底部对齐即可。

    Box {
        HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
            CarouselItem(itemList[page])
        }
    
        DotIndicators(
            pageCount = pageCount,
            pagerState = pagerState,
            modifier = Modifier.align(Alignment.BottomCenter)
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们创建了一个名为DotIndicators的可组合组件。它需要两个参数:页数(pageCount)和页面状态(pageState)。我们使用页数(pageCount)添加相同数量的指示器,而使用页面状态(pageState)使选定的点显示更暗。内部实现如下:

    @Composable
    fun DotIndicators(
        ...,
        modifier: Modifier
    ) {
        Row(modifier = modifier) {
            repeat(pageCount) { iteration ->
                val color = if (pagerState.currentPage == iteration) selectedColor else unselectedColor
                Box(
                    modifier = Modifier
                        .clip(CircleShape)
                        .background(color)
                )
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    上面指示器效果如下:
    指示器效果

    为HorizontalPager页面实现自动滚动

    目前,我们的设置要求用户手动水平滑动以导航到下一页。为了增强用户体验,我们可以实现自动滚动。我们通过利用HorizontalPager提供的Coroutine、LaunchedEffect和PagerState来实现这一点。
    以下是一个完全具备功能的示例实现:

    @Composable
    fun AwesomeCarousel(
        pageCount: Int = 10,
        pagerState: PagerState = rememberPagerState(),
        autoScrollDuration: Long = 3000L
    ) {
        val isDragged by pagerState.interactionSource.collectIsDraggedAsState()
        if (isDragged.not()) {
            with(pagerState) {
                var currentPageKey by remember { mutableStateOf(0) }
                LaunchedEffect(key1 = currentPageKey) {
                    launch {
                        delay(timeMillis = autoScrollDuration)
                        val nextPage = (currentPage + 1).mod(pageCount)
                        animateScrollToPage(page = nextPage)
                        currentPageKey = nextPage
                    }
                }
            }
        }
    
        HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
            CarouselItem(itemList[page])
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    代码片段中的主要要点是:

    1. LaunchedEffectLaunchedEffect 是 Jetpack Compose 提供的一种特殊效果,允许我们在协程中执行副作用,当特定键值发生改变时触发。在这种情况下,key1 参数设置为 currentPageKey,这意味着只要 currentPageKey 的值发生更改,该效果就会被触发。在LaunchedEffect块内,我们使用 launch 函数启动一个新的协程。这个协程在合成过程中异步执行并且独立于合成进程。

    2. delay(timeMillis = autoScrollDuration):这一行引入了一个延迟,协程会在 autoScrollDuration 变量指定的持续时间内暂停,然后继续执行下一行代码。在暂停后,我们再次更改键,这导致协程重新启动,因此这意味着 LaunchedEffect 内部的块将无限次地执行。

    3. isDragged.not():此代码段确保仅在不拖动页面时执行后续操作。它控制自动滚动行为,避免了用户交互和自动滚动过程之间的冲突。例如,如果用户正在滑动页面,自动滚动将停止。

    利用动画增强页面外观

    实现的最后一部分着重于增强轮播页面的视觉过渡效果。通过利用graphicLayer修饰器,我们可以实现平滑无缝的过渡效果。关键动画包括在淡出旧项的同时淡入新项,以及在项接近或远离中心时进行缩放。

    以下代码计算当前页面和目标页面之间的偏移量,然后使用此偏移量在0.7和1之间插值出一个转换值。然后将此转换值应用于graphicLayeralphascaleY属性。

    fun Modifier.carouselTransition(page: Int, pagerState: PagerState) =
        graphicsLayer {
            val pageOffset =
                ((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue
    
            val transformation =
                lerp(
                    start = 0.7f,
                    stop = 1f,
                    fraction = 1f - pageOffset.coerceIn(0f, 1f)
                )
            alpha = transformation
            scaleY = transformation
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 我们计算了在页面分页器中当前页面和目标页面之间的偏移量。它考虑了整个页面差异(pagerState.currentPage-page)和分数(pagerState.currentPageOffsetFraction),后者表示在页面之间转换的进度。绝对值确保偏移量始终为正数。
    2. lerp函数用于基于分数在起始点和结束点之间插值计算一个值。在这种情况下,起始值为0.7f,停止值为1f,而分数则被计算为1f - pageOffset.coerceIn(0f, 1f)。coerceIn函数可以确保分数保持在0到1的范围内。
    3. alpha = transformationtransformation的值分配给了graphicLayeralpha属性。将alpha设置为介于0和1之间的值可以确定图层的透明度。值为1表示图层完全不透明,而值为0表示它完全透明。
    4. scaleY = transformation同样,这行将transformation的值分配给了graphicLayerscaleY属性。调整scaleY属性会垂直缩放图层。值为1表示图层的原始大小,而小于1的值会减少垂直比例,大于1的值会增加垂直比例。
      我们在HorizontalPageritem modifier上利用graphicLayer扩展函数。这使我们可以对每个旋转木马项目应用变换并创建效果。
    HorizontalPager() { page ->
        val page = list[page]
        CarouselItem(
            item = page,
            modifier = Modifier.carouselTransition(page, pagerState)
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    电影App效果

    github地址

    https://github.com/TheSomeshKumar/Flixplorer

  • 相关阅读:
    在本地PC运行 Stable Diffusion 2.0
    docker命令
    在排序数组中查找元素的第一个和最后一个位置
    2022-08-19 第六小组 瞒春 学习笔记
    【ArcGIS微课1000例】0054:尺寸注记的创建与编辑
    cpp primer plus笔记07-内存模型和命名空间
    Vscode-Git graph怎么看?
    一文解读Docker 网络Network
    图文详解Linux基础经典教程(09)——部署项目至CentOS
    技术对接48
  • 原文地址:https://blog.csdn.net/u011897062/article/details/131032644