You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 12:40:21