如何让MotionLayout双向过渡均实现先快后慢的cubic插值效果?
问题描述
在MotionLayout中设置motionInterpolator="cubic(0.25,0,0,1)"后,start到half_open的正向过渡为先快后慢,但half_open到start的反向过渡却变为先慢后快。期望双向过渡都实现先快后慢的效果。
尝试过反转Transition的起止状态处理,但适配折叠屏场景时,初始状态可能为half_open,会导致动画重复执行;查看源码发现setTransition()方法本身会自动执行transitionToEnd,不符合需求。
相关Transition配置如下:
<Transition android:id="@+id/start_to_half_open" motion:constraintSetEnd="@+id/half_open" motion:constraintSetStart="@id/start" motion:duration="500" motion:transitionDisable="false" motion:layoutDuringTransition="callMeasure" motion:motionInterpolator="cubic(0.25,0,0,1)"> <KeyFrameSet> <!-- 省略原有KeyAttribute和KeyPosition配置 --> </KeyFrameSet> </Transition> <Transition android:id="@+id/half_open_to_start" motion:constraintSetEnd="@+id/start" motion:constraintSetStart="@id/half_open" motion:duration="500" motion:motionInterpolator="cubic(0.25,0,0,1)"> <KeyFrameSet> <!-- 省略原有KeyAttribute和KeyPosition配置 --> </KeyFrameSet> </Transition>
代码调用:
mMotionLayout.setTransition(R.id.half_open,R.id.start);
解决方案
1. 为反向Transition配置对应先快后慢的插值器
MotionLayout默认会在反向播放时反转插值器的时间进度,导致曲线变为先慢后快。要让反向动画也保持先快后慢,需为half_open_to_start设置反向对应的贝塞尔曲线:
将该Transition的motionInterpolator改为cubic(0.25,1,0,0),此曲线会让反向动画从half_open到start时,一开始快速变化,随后逐渐减速,实现先快后慢的效果。
修改后的反向Transition配置:
<Transition android:id="@+id/half_open_to_start" motion:constraintSetEnd="@+id/start" motion:constraintSetStart="@id/half_open" motion:duration="500" motion:motionInterpolator="cubic(0.25,1,0,0)"> <KeyFrameSet> <!-- 保留原有KeyAttribute和KeyPosition配置 --> </KeyFrameSet> </Transition>
2. 使用setCurrentState设置初始状态,避免自动触发动画
setTransition()方法会自动触发向end状态的过渡,不符合折叠屏初始状态可能为half_open的需求。改用setCurrentState()设置初始状态,该方法仅切换状态而不触发动画:
// 根据折叠屏实际状态设置初始状态,例如此处初始为half_open mMotionLayout.setCurrentState(R.id.half_open);
3. 禁用自动过渡,确保使用自定义双向Transition
在MotionScene根节点添加motion:autoTransition="none",禁止系统自动生成反向Transition,强制使用你定义的两个独立Transition:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto" motion:autoTransition="none"> <!-- 你的ConstraintSet和Transition配置 --> </MotionScene>
4. 手动触发过渡
需要切换状态时,使用transitionToState()方法,避免使用setTransition():
// 从half_open切换到start mMotionLayout.transitionToState(R.id.start); // 从start切换到half_open mMotionLayout.transitionToState(R.id.half_open);
内容的提问来源于stack exchange,提问作者shallow light

