Android6.0 WMS(十) WMS窗口动画从设置到显示框架

       在前一文中,我们分析了Activity组件的切换过程。从这个过程可以知道,所有参与切换操作的窗口都会被设置切换动画。事实上,一个窗口在打开(关闭)的过程中,除了可能会设置切换动画之外,它本身也可能会设置有进入(退出)动画。再进一步地,如果一个窗口是附加在另外一个窗口之上的,那么被附加窗口所设置的动画也会同时传递给该窗口。

在之前WMS的第六篇博客窗口管理,主要从VSync信号作为切入点分析窗口动画的流程。这里我们就详细分析WindowManagerService服务显示窗口动画的原理。

        在Android系统中,窗口动画的本质就是对原始窗口施加一个变换(Transformation)。在线性数学中,对物体的形状进行变换是通过乘以一个矩阵(Matrix)来实现,目的就是对物体进行偏移、旋转、缩放、切变、反射和投影等。因此,给窗口设置动画实际上就给窗口设置一个变换矩阵(Transformation Matrix)。

        如前所述,一个窗口在打开(关闭)的过程,可能会被设置三个动画,它们分别是窗口本身所设置的进入(退出)动画(Self Transformation)、从被附加窗口传递过来的动画(Attached Transformation),以及宿主Activity组件传递过来的切换动画(App Transformation)。这三个Transformation组合在一起形成一个变换矩阵,以60fps的速度应用在窗口的原始形状之上,完成窗口的动画过程。

        窗口的变换矩阵是应用在窗口的原始位置和大小之上的,因此,在显示窗口的动画之前,除了要给窗口设置变换矩阵之外,还要计算好窗口的原始位置和大小,以及布局和绘制好窗口的UI。我们在之前博客已经分析过窗口的位置和大小计算过程以及窗口UI的布局和绘制过程了,本文主要关注窗口动画的设置、合成和显示过程。这三个过程通过以下四个部分的内容来描述:
       1. 窗口动画的设置过程
       2. 窗口动画的显示框架
       3. 窗口动画的推进过程
       4. 窗口动画的合成过程
       其中,窗口动画的设置过程包括上述三个动画的设置过程,窗口动画的推进过程是指定动画的一步一步地迁移的过程,窗口动画的合成过程是指上述三个动画组合成一个变换矩阵的过程,后两个过程包含在了窗口动画的显示框架中。

一. 窗口动画的设置过程


        窗口被设置的动画虽然可以达到三个,但是这三个动画可以归结为两类,一类是普通动画,例如,窗口在打开过程中被设置的进入动画和在关闭过程中被设置的退出动画,另一类是切换动画。其中,Self Transformation和Attached Transformation都是属于普通动画,而App Transformation属于切换动画。接下来我们就分别分析这两种类型的动画的设置过程。

1.1 普通动画的设置

窗口在打开的过程中,是通过调用WindowState类的成员函数performShowLocked来实现的,WindowStateAnimator类的成员函数performShowLocked调用了applyEnterAnimationLocked函数来给当前正在处理的窗口设置一个进入动画,如下所示:
    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);
        }
    }
       如果mEnterAnimationPending的值等于true,那么就说明它所描述的窗口正在等待显示,也就是正处于不可见到可见状态的过程中,那么函数applyEnterAnimationLocked就会对该窗口设置一个类型为WindowManagerPolicy.TRANSIT_ENTER的动画,否则的话,就会对该窗口设置一个类型为WindowManagerPolicy.TRANSIT_SHOW的动画。
        确定好窗口的动画类型之后,就调用另外一个成员函数applyAnimationLocked来为窗口创建一个动画了。接下来我们先分析窗口在关闭的过程中所设置的动画类型,然后再来分析applyAnimationLocked函数的实现。
        当应用程序进程请求WindowManagerService服务刷新一个窗口的时候,会调用到WindowManagerService类的成员函数relayoutWindow。WindowManagerService类的成员函数relayoutWindow在执行的过程中,如果发现需要将一个窗口从可见状态设置为不可见状态时,也就是发现需要关闭一个窗口时,就会对该窗口设置一个退出动出,如下所示:
            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;
                        }
WindowState对象win描述的便是要刷新的窗口。当参数viewVisibility的值不等于View.VISIBLE时,就说明要将WindowState对象win所描述的窗口设置为不可见。另一方面,如果WindowState对象win的成员变量mAppToken的值不等于null,并且这个成员变量所指向的一个AppWindowToken对象的成员变量clientHidden的值等于true,那么就说明WindowState对象win所描述的窗口是一个与Activity组件相关的窗口,并且该Activity组件是处于不可见状态的。在这种情况下,也需要将WindowState对象win所描述的窗口设置为不可见。
        一旦WindowState对象win所描述的窗口要设置为不可见,就需要考虑给它设置一个退出动画,不过有四个前提条件:
        1. 该窗口有一个绘图表面,即WindowState对象win的成员变量mSurface的值不等于null;
        2. 该窗口的绘图表面不是处于等待销毁的状态,即WindowState对象win的成员变量mSurfacePendingDestroy的值不等于true;
        3. 该窗口不是处于正在关闭的状态,即WindowState对象win的成员变量mExiting的值不等于true;
        4. 该窗口当前正在处于可见的状态,即WindowState对象win的成员isWinVisibleLw的返回值等于true。
        在满足上述四个条件的情况下,就说明WindowState对象win所描述的窗口的状态要由可见变为不可见,因此,就需要给它设置一个退出动画,即一个类型为WindowManagerPolicy.TRANSIT_EXIT的动画,这同样是通过调用WindowManagerService类的成员函数applyAnimationLocked来实现的。
        从上面的分析就可以知道,无论是窗口在打开时所需要的进入动画,还是窗口在关闭时所需要的退出动画,都是通过调用applyAnimationLocked来设置的,它的实现如下所示:
    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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值