很多用户为了手机用起来感觉上快,在开发者选项里把系统动画给关了,即把那3个缩放比例改成了0,系统默认一般是1x。个人建议调到0.5x就很合适了,没必要关闭,否则某些动效比较多的App体验会很差。
本文的问题就是,当开发者选项中的动画缩放比例被全部调成0后,App中部分设置了duration的属性动画就会失效,表现为直接从开头跳到了结尾,没有动画过程。

我们先分析下为什么会这样。
看ValueAnimator(/frameworks/base/core/java/android/animation/ValueAnimator.java)的部分源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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也就是开发者选项中设置的动画缩放倍数。
我们只要在动画初始化后,通过反射来重置一下这个静态变量即可:

1
2
3
4
5
6
7
8
9
10
11
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中)。