简介

开发者选项中动画设置:

  • 窗口动画缩放(Windos animation scale)
  • 过渡动画缩放(Transition animation scale)
  • 动画程序时长缩放(Animation duration scale)
    分别对应Window动画(非Activity窗口。比如,Dialog、toast、自定义浮窗、输入法等窗口)、Activity动画(Activity窗口。Activity窗口)、View动画(比如View属性动画、水波纹背景动画)

原理

Setting设置动画

代码路径

1
packages/apps/Settings/src/com/android/settings/DevelopmentSettings.java

WindowManagerService提供了setAnimationScale() API供Setting使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IWindowManager;

IWindowManager mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
try {
float scale = 0; //动画时长 [0,0.5,1,1.5,2,5,10]对应设置的[关闭,0.5x,1x.1.5x,2x,5x,10x]
mWindowManager.setAnimationScale(0, scale); //设置窗口动画缩放
mWindowManager.setAnimationScale(1, scale); //设置过渡动画缩放
mWindowManager.setAnimationScale(2, scale); //动画程序时长缩放
} catch (RemoteException e) {

}

// 声明权限
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>

setAnimationScale()函数

setAnimationScale()函数在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

@Override
public void setAnimationScale(int which, float scale) {
if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
"setAnimationScale()")) {
throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
}

scale = fixScale(scale);
switch (which) {
case 0: mWindowAnimationScaleSetting = scale; break;
case 1: mTransitionAnimationScaleSetting = scale; break;
case 2: mAnimatorDurationScaleSetting = scale; break;
}

// Persist setting
mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE);

打印dump信息

执行命令

1
adb shell dumpsys window w -a

Window动画时长设置

Window动画的设置是通过frameworks\base\services\core\java\com\android\server\wm\WindowStateAnimator.java文件的setAnimation()函数来完成的

1
2
3
4
5
public void setAnimation(Animation anim, long startTime) {
......
mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
......
}

调用Animation.scaleCurrentDuration()函数来重置动画时长为duration*scale

Activity动画时长设置

Activity切换动画的设置是通过frameworks\base\services\core\java\com\android\server\wm\AppWindowAnimator.java文件的setAnimation()函数来完成的

1
2
3
4
5
public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
......
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
......
}

调用Animation.scaleCurrentDuration()函数来重置动画时长为duration*scale

View动画时长设置

View动画时长是通过ValueAnimator.sDurationScale静态变量来控制的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
sWindowManagerService = getWindowManagerService();
ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
}
}
return sWindowManagerService;
}
}

在进程启动后第一次调用getWindowManagerService()时便会从WMS中获取缩放值,然后保存到ValueAnimator.sDurationScale中
如果Setting中更新了View动画缩放因子,那么WMS中调用dispatchNewAnimatorScaleLocked()函数后会回调上层应用的onAnimatorScaleChanged()接口,通知应用View的动画时长Scale更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}

对于View动画,如果动画时长使用了ValueAnimator.sDurationScale,那么必然受”Animator duration scale”控制