两个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
相关产品推荐
相关产品推荐

