60行制作一个JetpackCompose Material3轮播图

制作一个JetpackCompose Material3轮播图

视频教程

JetpackCompose快速制作轮播图(banner)_哔哩哔哩_bilibili

效果

轮播图效果.png

网络图片加载库Coil

implementation("io.coil-kt:coil-compose:2.4.0")

核心组件

Coil(AsyncImage)+HorizontalPager+LaunchedEffect

测试数据

 val imgList = listOf(
     "https://img.alicdn.com/imgextra/i2/O1CN01lFNk4Z1JDkviZe8EG_!!6000000000995-2-tps-846-472.png",
     "https://img.alicdn.com/imgextra/i4/O1CN01GOlEEW1zidUliI8O7_!!6000000006748-2-tps-846-472.png",
     "https://img.alicdn.com/imgextra/i4/O1CN01LHWdyp1yvAidcNMDR_!!6000000006640-0-tps-846-472.jpg"
 )

状态变量

setContent {
    //HorizontalPager的状态
    val pagerState = rememberPagerState()
    //当前滚动到了哪哪一个页面
    val nowPageIndex = pagerState.currentPage
    //用于点击指示器的时候启动协程进行HorizontalPager位置手动更新
    val scope = rememberCoroutineScope()
    //......
}

Banner结构

Box { //这样指示器就可以覆盖在轮播图上
    HorizontalPager(
        state = pagerState,
        //不让一个图最宽,留空可以一个页面渲染出多个图
        contentPadding = PaddingValues(horizontal = 50.dp), 
        modifier = Modifier
            .height(180.dp)
            .padding(top = 15.dp),
        pageCount = imgList.size
    ) { index ->
       //激活项的缩放过渡
        val imgScale by animateFloatAsState(
            targetValue = if (nowPageIndex == index) 1f else 0.8f,
            animationSpec = tween(300), label = ""
        )
       //使用Coil加载网络图片
        AsyncImage(
            modifier = Modifier
                .fillMaxSize()
                .scale(imgScale)
                .clip(
                    RoundedCornerShape(10.dp) //轮播图圆角裁剪
                ),
            model = ImageRequest
                .Builder(LocalContext.current)
                .data(imgList[index])
                .scale(Scale.FILL)
                .build(),
            contentDescription = "图片$index",
            contentScale = ContentScale.FillBounds
        )
    }
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .align(Alignment.BottomCenter)
            .padding(bottom = 5.dp),
        horizontalArrangement = Arrangement.Center
    ) {
        //循环渲染指示器
        imgList.indices.forEach { radioIndex ->
            RadioButton(selected = nowPageIndex == radioIndex, onClick = {
                //点击的时候启动协程手动进行HorizontalPager的页面滚动
                scope.launch {
                    pagerState.animateScrollToPage(radioIndex)
                }
            })
        }
    }
}

定时器

//观察pagerState.settledPage,已确保滚动结束再进行下一次自动轮播
LaunchedEffect(pagerState.settledPage) {
    delay(1500)//延迟1.5s滚动
    //判断下一次滚动索引是否越界
    val scroller =
        if (pagerState.currentPage + 1 == imgList.size) 0 else pagerState.currentPage + 1
    //手动进行HorizontalPager的页面滚动
    pagerState.animateScrollToPage(scroller)
}

完整代码

class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val imgList = listOf(
            "https://img.alicdn.com/imgextra/i2/O1CN01lFNk4Z1JDkviZe8EG_!!6000000000995-2-tps-846-472.png",
            "https://img.alicdn.com/imgextra/i4/O1CN01GOlEEW1zidUliI8O7_!!6000000006748-2-tps-846-472.png",
            "https://img.alicdn.com/imgextra/i4/O1CN01LHWdyp1yvAidcNMDR_!!6000000006640-0-tps-846-472.jpg"
        )
        setContent {
            val pagerState = rememberPagerState()
            val nowPageIndex = pagerState.currentPage
            val scope = rememberCoroutineScope()
            LaunchedEffect(pagerState.settledPage) {
                delay(1500)
                val scroller =
                    if (pagerState.currentPage + 1 == imgList.size) 0 else pagerState.currentPage + 1
                pagerState.animateScrollToPage(scroller)
            }
            ComposeLearnTheme {
                Box {
                    HorizontalPager(
                        state = pagerState,
                        contentPadding = PaddingValues(horizontal = 50.dp),
                        modifier = Modifier
                            .height(180.dp)
                            .padding(top = 15.dp),
                        pageCount = imgList.size
                    ) { index ->
                        val imgScale by animateFloatAsState(
                            targetValue = if (nowPageIndex == index) 1f else 0.8f,
                            animationSpec = tween(300), label = ""
                        )
                        AsyncImage(
                            modifier = Modifier
                                .fillMaxSize()
                                .scale(imgScale)
                                .clip(
                                    RoundedCornerShape(10.dp)
                                ),
                            model = ImageRequest
                                .Builder(LocalContext.current)
                                .data(imgList[index])
                                .scale(Scale.FILL)
                                .build(),
                            contentDescription = "图片$index",
                            contentScale = ContentScale.FillBounds
                        )
                    }
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .align(Alignment.BottomCenter)
                            .padding(bottom = 5.dp),
                        horizontalArrangement = Arrangement.Center
                    ) {
                        imgList.indices.forEach { radioIndex ->
                            RadioButton(selected = nowPageIndex == radioIndex, onClick = {
                                scope.launch {
                                    pagerState.animateScrollToPage(radioIndex)
                                }
                            })
                        }
                    }
                }
            }
        }
    }
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYg6Ijor' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片