问题现象
- 手机进行“开发者选项”→“Animator 时长缩放”→“关闭动画”流程设置后,发现app部分场景会直接从初始状态进入到结束状态,过渡动画消失了
问题分析
-
ValueAnimator(/frameworks/base/core/java/android/animation/ValueAnimator.java)源码分析:由源码可知,动画是否结束取决于animateBasedOnTime函数,animateBasedOnTime函数中调用getScaledDuration获取时长缩放,由于开发者选项中奖“Animator 时长缩放”修改为“关闭动画”,导致从getScaledDuration获取到的时长缩放为0,此时animateBasedOnTime会直接跳到结束,进而给finished赋值为true,表示动画已执行完毕
public final boolean doAnimationFrame(long frameTime) { ... if (mLastFrameTime < 0) { if (mSeekFraction >= 0) { long seekTime = (long) (getScaledDuration() * mSeekFraction); mStartTime = frameTime - seekTime; mSeekFraction = -1; } mStartTimeCommitted = false; } mLastFrameTime = frameTime; final long currentTime = Math.max(frameTime, mStartTime); // “关闭动画”后,此时finished会立刻为true,没有过渡时长 boolean finished = animateBasedOnTime(currentTime); if (finished) { // 马上调用结束动画的函数,导致用户看到过渡动画消失的现象 endAnimation(); } return finished; } boolean animateBasedOnTime(long currentTime) { boolean done = false; if (mRunning) { // 由于开发者选项中更改为“关闭动画”,导致scaledDuration的值也为0 final long scaledDuration = getScaledDuration(); ... if (scaledDuration == 0) { // 0 duration animator, ignore the repeat count and skip to the end done = true; } ... } return done; } private long getScaledDuration() { // 开发者选项中修改“Animator 时长缩放”为“关闭动画”时,会使sDurationScale值变为0 return (long)(mDuration * sDurationScale); }
问题解决
-
在动画初始化时,通过反射来重置一下这个静态变量即可
// java 版本 private void resetAnimatorDurationScale() { try { Field field = ValueAnimator.class.getDeclaredField("sDurationScale"); field.setAccessible(true); if (field.getFloat(null) == 0) { field.setFloat(null, 1); } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } // kotlin 版本 private fun resetAnimatorDurationScale(){ try { val field = ValueAnimator::class.java.getDeclaredField("sDurationScale") field.isAccessible = true if (field.getFloat(null) == 0f) { field.setFloat(null, 1f) } } catch (e: Exception) { e.printStackTrace() } }
-
上述方法存在缺陷:如果进入到开发者选项将“Animator 时长缩放”由“关闭动画”改为“1.0”,再由“1.0”改为“关闭动画”,app还是会出现部分场景过渡动画消失的现象。解决方法:在app中给ValueAnimator对象添加监听addListener,然后在onAnimationStart方法中调用一下resetAnimatorDurationScale重置参数