在前一文中,我们分析了Activity组件的切换过程。从这个过程可以知道,所有参与切换操作的窗口都会被设置切换动画。事实上,一个窗口在打开(关闭)的过程中,除了可能会设置切换动画之外,它本身也可能会设置有进入(退出)动画。再进一步地,如果一个窗口是附加在另外一个窗口之上的,那么被附加窗口所设置的动画也会同时传递给该窗口。
在之前WMS的第六篇博客窗口管理,主要从VSync信号作为切入点分析窗口动画的流程。这里我们就详细分析WindowManagerService服务显示窗口动画的原理。
在Android系统中,窗口动画的本质就是对原始窗口施加一个变换(Transformation)。在线性数学中,对物体的形状进行变换是通过乘以一个矩阵(Matrix)来实现,目的就是对物体进行偏移、旋转、缩放、切变、反射和投影等。因此,给窗口设置动画实际上就给窗口设置一个变换矩阵(Transformation Matrix)。
如前所述,一个窗口在打开(关闭)的过程,可能会被设置三个动画,它们分别是窗口本身所设置的进入(退出)动画(Self Transformation)、从被附加窗口传递过来的动画(Attached Transformation),以及宿主Activity组件传递过来的切换动画(App Transformation)。这三个Transformation组合在一起形成一个变换矩阵,以60fps的速度应用在窗口的原始形状之上,完成窗口的动画过程。
一. 窗口动画的设置过程
1.1 普通动画的设置
void applyEnterAnimationLocked() {
final int transit;
if (mEnterAnimationPending) {
mEnterAnimationPending = false;
transit = WindowManagerPolicy.TRANSIT_ENTER;
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
applyAnimationLocked(transit, true);
if (mService.mAccessibilityController != null
&& mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
}
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
......
} else {
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
if (winAnimator.mSurfaceControl != null) {
if (!win.mExiting) {
surfaceChanged = true;
int transit = WindowManagerPolicy.TRANSIT_EXIT;//设置一个退出动画
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
if (win.isWinVisibleLw() &&
winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = isDefaultDisplay;
win.mExiting = true;
}
boolean applyAnimationLocked(int transit, boolean isEntrance) {
if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)
|| mKeyguardGoingAwayAnimation) {
if (mAnimation != null && mKeyguardGoingAwayAnimation
&& transit == WindowManagerPolicy.TRANSIT_PREVIEW_DONE) {
applyFadeoutDuringKeyguardExitAnimation();
}
return true;
}
if (mService.okToDisplay()) {
int anim = mPolicy.selectAnimationLw(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != 0) {
a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
} else {
switch (transit) {
case WindowManagerPolicy.TRANSIT_ENTER:
attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
break;
case WindowManagerPolicy.TRANSIT_EXIT:
attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
break;
case WindowManagerPolicy.TRANSIT_SHOW:
attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
bre