很多用户为了手机用起来感觉上快,在开发者选项里把系统动画给关了,即把那3个缩放比例改成了0,系统默认一般是1x。个人建议调到0.5x就很合适了,没必要关闭,否则某些动效比较多的App体验会很差。 本文的问题就是,当开发者选项中的动画缩放比例被全部调成0后,App中部分设置了duration的属性动画就会失效,表现为直接从开头跳到了结尾,没有动画过程。
我们先分析下为什么会这样。 看ValueAnimator(/frameworks/base/core/java/android/animation/ValueAnimator.java)的部分源码:
public final boolean doAnimationFrame(long frameTime) {
......
if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction); // 开发者选项改后此处为0
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false;
}
mLastFrameTime = frameTime;
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
final long scaledDuration = getScaledDuration(); // 开发者选项改后此处为0
......
if (scaledDuration == 0) {
// 0 duration animator, ignore the repeat count and skip to the end
done = true;
} ......
}
return done;
}
private long getScaledDuration() {
return (long)(mDuration * sDurationScale); // 开发者选项修改直接影响sDuration的值
}
动画绘制是否结束取决于animateBasedOnTime方法,源码也注释到,如果duration为0,会忽略repeat count直接跳到结束状态。这里的sDurationScale也就是开发者选项中设置的动画缩放倍数。 我们只要在动画初始化后,通过反射来重置一下这个静态变量即可:
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();
}
}
但很显然,系统会在应用进程重启后第一次调用getWindowManagerService时重设这个值,所以必须得每次启动后都设置一次(比如在主Activity的onCreate中)。