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

Jetpack Compose中Instagram风格故事指示器动画控制问题

解决Jetpack Compose Instagram故事组件滑动回退时的动画控制问题

核心问题分析

原代码的主要问题在于:

  • currentPage与PagerState.currentPage未同步,滑动页面时本地状态更新不及时
  • 指示器动画缺乏取消机制,切换页面后旧动画仍在运行,导致进度状态混乱
  • 未明确区分“已完成”“当前播放”“未开始”三种指示器状态

解决方案代码

修改后的Stories组件

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Stories(data: List<Int>) {
    val pagerState = rememberPagerState(
        initialPage = 0,
        initialPageOffsetFraction = 0f
    )
    var currentPage by remember { mutableStateOf(0) }
    val coScope = rememberCoroutineScope()

    // 同步PagerState的页面变化到本地currentPage
    LaunchedEffect(pagerState.currentPage) {
        currentPage = pagerState.currentPage
    }

    Box(
        modifier = Modifier
            .padding(16.dp)
            .clip(RoundedCornerShape(8.dp))
    ) {
        Story(state = pagerState, data = data)
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(start = 16.dp, top = 16.dp, end = 16.dp),
            horizontalArrangement = Arrangement.spacedBy(4.dp)
        ) {
            data.forEachIndexed { index, _ ->
                Indicators(
                    modifier = Modifier
                        .height(4.dp)
                        .weight(1f),
                    isCurrentPage = currentPage == index,
                    isCompleted = index < currentPage,
                    onAnimateComplete = {
                        coScope.launch {
                            if (currentPage < data.size - 1) {
                                currentPage++
                                pagerState.animateScrollToPage(currentPage)
                            }
                        }
                    }
                )
            }
        }
    }
}

修改后的Indicators组件

@Composable
fun Indicators(
    modifier: Modifier,
    isCurrentPage: Boolean,
    isCompleted: Boolean,
    onAnimateComplete: () -> Unit
) {
    var progress by remember { mutableStateOf(0f) }
    val animationScope = rememberCoroutineScope()
    var animationJob by remember { mutableStateOf<Job?>(null) }

    // 根据页面状态重置或启动动画
    LaunchedEffect(isCurrentPage, isCompleted) {
        // 取消之前的动画,避免状态混乱
        animationJob?.cancel()
        
        when {
            isCompleted -> {
                progress = 1f
            }
            isCurrentPage -> {
                progress = 0f
                // 启动新的进度动画,总时长5秒(可自定义)
                animationJob = animationScope.launch {
                    val totalDuration = 5000L
                    val startTime = System.currentTimeMillis()
                    
                    while (System.currentTimeMillis() - startTime < totalDuration) {
                        progress = (System.currentTimeMillis() - startTime) / totalDuration.toFloat()
                        delay(16) // 保持60fps平滑动画
                    }
                    
                    progress = 1f
                    onAnimateComplete()
                }
            }
            else -> {
                progress = 0f
            }
        }
    }

    LinearProgressIndicator(
        modifier = modifier.clip(RoundedCornerShape(12.dp)),
        color = Color.White,
        trackColor = Color.Gray.copy(alpha = 0.5f),
        progress = progress
    )
}

Story组件保持不变

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Story(state: PagerState, data: List<Int>) {
    HorizontalPager(
        modifier = Modifier,
        state = state,
        pageCount = data.size
    ) {
        Image(
            modifier = Modifier.height(440.dp),
            painter = painterResource(id = data[it]),
            contentDescription = "StoryImage",
            contentScale = ContentScale.FillBounds,
        )
    }
}

关键修改说明

  1. 页面状态同步:添加LaunchedEffect监听PagerState.currentPage,确保滑动页面时本地currentPage实时更新,让指示器状态与当前页面保持一致。
  2. 动画生命周期管理:在Indicators中引入animationJob,每次状态变化时先取消旧动画,再根据当前状态启动新动画或重置进度,彻底解决切换页面后旧动画继续运行的问题。
  3. 状态明确区分:将指示器分为三种状态:
    • 已完成(isCompleted):进度直接设为1f
    • 当前页面(isCurrentPage):从0开始播放进度动画
    • 未开始:进度重置为0f
  4. 平滑动画实现:改用系统时间计算进度,替代原有的固定步长累加,动画更平滑,且总时长可灵活调整(默认5秒与Instagram一致)。

内容的提问来源于stack exchange,提问作者Hrishi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:24:54