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

两个MotionLayout动画同时启动却串行执行失步,是否有可用的同步API?

多个MotionLayout动画同步解决方案

问题原因

MotionLayout默认每个动画实例单独基于Choreographer帧回调计算进度,连续调用transitionToEnd()/transitionToStart()时,可能因帧调度差异导致两个动画的启动时间差了1个Vsync周期(约16ms),表现为动画先后执行、不同步。

方案1:统一用ValueAnimator驱动(全版本兼容,稳定性最高)

不使用MotionLayout自带的过渡启动方法,通过同一个ValueAnimator同时更新所有MotionLayout的进度,保证进度100%同步:

// 初始化全局统一驱动的Animator
val syncAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
    duration = 300 // 和MotionScene中配置的时长保持一致
    interpolator = LinearInterpolator() // 和MotionScene中配置的插值器保持一致
    addUpdateListener {
        val currentProgress = it.animatedValue as Float
        // 同时给所有需要同步的MotionLayout设置进度
        viewBinding.playerBottomLl.progress = currentProgress
        // 你的另一个MotionLayout实例也在这里同步设置进度
        // viewBinding.otherMotionLayout.progress = currentProgress
    }
}

// 触发动画时调用
override fun onVisibilityChanged(isVisible: Boolean) {
    if (syncAnimator.isRunning) {
        syncAnimator.cancel()
    }
    if (isVisible) {
        syncAnimator.start()
    } else {
        syncAnimator.reverse()
    }
}

如果需要监听动画完成事件,可给统一驱动的ValueAnimator添加Animator.AnimatorListener实现,替代原有的MotionLayout.TransitionListener。

方案2:使用高版本MotionLayout的指定启动时间API(2.1.0及以上支持)

2.1.0版本后MotionLayout新增了支持指定启动时间的过渡API,给所有要同步的动画传入同一个启动时间戳即可强制同步:

override fun onVisibilityChanged(isVisible: Boolean) {
    val targetState = if (isVisible) R.id.end else R.id.start
    // 所有动画共用同一个启动时间戳
    val sameStartTime = System.currentTimeMillis()
    viewBinding.playerBottomLl.transitionToState(targetState, 300, sameStartTime)
    // 你的另一个MotionLayout也用同样参数调用
    // viewBinding.otherMotionLayout.transitionToState(targetState, 300, sameStartTime)
}

注意事项

  • 所有需要同步的MotionLayout的MotionScene配置中的duration、interpolator必须完全一致
  • 避免在动画执行过程中调用其他修改MotionLayout约束的方法,避免进度冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 19:06:10