制造一个JetpackCompose Material3轮播

视频教程

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

作用

60行制作一个JetpackCompose Material3轮播图

网络图片加载库Coil

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

核心组件

Coil(AsyncImage)+HorizontalPager+LaunchedEffect

测试数据

 val imgList = listOf(
     "https://www.6hu.cc/files/2023/07/1689363185-921c678c98763f5.png",
     "https://www.6hu.cc/files/2023/07/1689363192-2568af85285a6e3.png",
     "https://www.6hu.cc/files/2023/07/1689363198-aad6753967406ad.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://www.6hu.cc/files/2023/07/1689363185-921c678c98763f5.png",
            "https://www.6hu.cc/files/2023/07/1689363192-2568af85285a6e3.png",
            "https://www.6hu.cc/files/2023/07/1689363198-aad6753967406ad.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)
                                }
                            })
                        }
                    }
                }
            }
        }
    }
}