求助:如何实现Jetpack Compose Horizontal Pager自动滚动
实现Jetpack Compose Horizontal Pager自动滚动
要让Horizontal Pager实现自动滚动并支持循环切换,同时避免和用户手动滑动冲突,可通过以下步骤修改代码:
1. 在DashboardScreen中添加自动滚动逻辑
通过LaunchedEffect启动协程定时触发滚动,同时添加触摸状态控制,用户交互时暂停自动滚动:
@OptIn(ExperimentalPagerApi::class, FlowPreview::class) @Composable fun DashboardScreen( navController: NavHostController = rememberAnimatedNavController(), datastore: DataStoreRepository, dashboardViewModel: DashboardViewModel = hiltViewModel(), ) { val state by dashboardViewModel.dashboardState.collectAsState() val username = datastore.getUsername().collectAsState(initial = "") val company = datastore.getUserCompany().collectAsState(initial = "") val pagerState = rememberPagerState() // 控制自动滚动的开关,触摸时暂停 val autoScrollEnabled = remember { mutableStateOf(true) } val bannerList = state.dashboard?.banner ?: emptyList() // 自动滚动协程逻辑 LaunchedEffect(bannerList.size, autoScrollEnabled.value) { // 只有banner数量大于1且允许自动滚动时才执行 if (bannerList.size <= 1 || !autoScrollEnabled.value) return@LaunchedEffect while (true) { delay(3000) // 3秒滚动一次,可根据需求调整间隔 val nextPage = (pagerState.currentPage + 1) % bannerList.size pagerState.animateScrollToPage(nextPage) } } Column( modifier = Modifier .fillMaxSize() .padding(horizontal = 8.dp, vertical = 20.dp) .padding(12.dp) .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { TopSectionDashboard(name = username.value, company = company.value) Spacer(modifier = Modifier.height(20.dp)) Box { state.dashboard?.let { BannerSlider( state = pagerState, listBanner = it.banner, onTouch = { isTouching -> autoScrollEnabled.value = !isTouching } ) Indicators( size = it.banner.size, index = pagerState.currentPage, modifier = Modifier.align(Alignment.BottomCenter) ) } } if (state.isLoading) { Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { CircularProgressIndicator() } } } }
2. 修改BannerSlider添加触摸监听
给Horizontal Pager添加触摸事件监听,传递触摸状态到父组件控制自动滚动开关:
@OptIn(ExperimentalPagerApi::class) @Composable fun BannerSlider( modifier: Modifier = Modifier, state: PagerState, listBanner: List<Banner>, onTouch: (Boolean) -> Unit, // 新增触摸回调 ) { HorizontalPager( count = listBanner.size, state = state, modifier = modifier .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() when (event.type) { PointerEventType.Press -> onTouch(true) PointerEventType.Release, PointerEventType.Exit -> onTouch(false) } } } } ) { page -> // 直接传入已有Banner对象,无需重新构造 BannerItem(item = listBanner[page]) } }
3. 优化BannerItem代码
移除多余的Banner对象构造,同时完善无障碍描述:
@Composable fun BannerItem(item: Banner, modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxWidth() .height(200.dp) ) { Image( painter = painterResource(id = R.drawable.background_dashboard_slider1), contentDescription = "Banner背景图", modifier = Modifier.fillMaxSize(), alignment = Alignment.Center, contentScale = ContentScale.Crop, ) Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .align(Alignment.CenterStart) .offset(y = (-20).dp) ) { Column(horizontalAlignment = Alignment.Start) { Text( text = item.text, fontFamily = Poppins, fontSize = 20.sp, color = Color.White, modifier = Modifier.padding(horizontal = 24.dp) ) Text( text = "${item.count}", fontFamily = Poppins, fontSize = 25.sp, fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier .padding(horizontal = 24.dp) .offset(y = (-10).dp) ) } AsyncImage( model = item.icon, contentDescription = "${item.text}图标", modifier = Modifier.size(120.dp), alignment = Alignment.CenterEnd ) } } }
核心逻辑说明
- 循环滚动:通过
(currentPage + 1) % bannerList.size计算下一页,实现从最后一页无缝回到第一页。 - 触摸冲突处理:监听触摸事件,用户滑动时暂停自动滚动,避免和手动操作冲突,触摸结束后恢复滚动。
- 协程生命周期:
LaunchedEffect依赖banner数量和滚动开关状态,当数据变化或开关切换时自动重启协程,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Yuba
相关产品推荐
相关产品推荐

