Android 7.1 GUI系统-窗口管理WMS-窗口动画、应用动画的加载(六)

来源:互联网 发布:mariadb与mysql的区别 编辑:程序博客网 时间:2024/06/15 11:18

窗口动画的加载:

Activity窗口显示的过程中,除了窗口的申请,窗口大小的计算,窗口层级的设置等,还有窗口切换过程中的启动窗口的添加、销毁,窗口切换动画。

启动窗口,当一个新的Activity启动时系统可能会先显示一个启动窗口,这个启动窗口会等到Activity的主界面显示出来后消失,主要是为应用启动做一个过度。

启动窗口跟普通窗口本质上没去区别,大概列出调用流程,不过详细分析:

当需要添加启动窗口时(言外之意就是启动窗口不是必须要添加的),在startActivityLocked时,会通过showStartingWindow()@ActivityRecord.java调用到setAppStartingWindow()@WindowManagerService.java,有WMS安排窗口的创建、添加过程,相关的消息是ADD_STARTING,会调用到addStartingWindow()@PhoneWindowManager.java

当启动窗口需要移除时,会发送消息REMOVE_STARTINGWMS,进一步调用removeStartingWindow()@PhoneWindowManager.java,处理销毁窗口相关的数据。



窗口的切换动画,总的就两类进入动画,退出动画。动画的属性有平移、旋转、缩放、透明度,这些属性可以组合使用,除了透明度是通过变换float值之外,其他三类属性都是用Matrix运算来实现的。

android中动画类型有好几种,如AppWindowAnimatorWindowAnimatorWindowStateAnimatorScreenRotationAnimation,原理都是一样的。接下来主要关注动画资源是怎么加载的,动画是怎么执行的。

动画资源的加载。

1)跟窗口有关的动画WindowStateAnimator,当一个Activity启动时,会调用到WMSrelayoutWindow来申请窗口,这其中就应用到窗口的切换动画,如果窗口进入动画,具体就是调用WindowStateAnimator.java中的函数applyEnterAnimationLocked

WindowStateAnimator除了处理surface相关的操作,还处理动画流程的跟踪。

void applyEnterAnimationLocked() @WindowStateAnimator.java{final int transit;if (mEnterAnimationPending) {mEnterAnimationPending = false;transit = WindowManagerPolicy.TRANSIT_ENTER;}else{transit = WindowManagerPolicy.TRANSIT_SHOW;}applyAnimationLocked(transit, true);}

从上面的函数中,进入动画分为TRANSIT_ENTERTRANSIT_SHOW两种,当mEnterAnimationPendingtrue时,程序执行TRANSIT_ENTER动画。mEnterAnimationPending的值是在WMS中设置的,一种情况是新添加窗口addWindow时:winAnimator.mEnterAnimationPending= true;还有一种情况是relayoutVisibleWindow时,可见状态从GONEVISIBLE

if (oldVisibility ==View.GONE)winAnimator.mEnterAnimationPending = true;


window相关的动画类型除了TRANSIT_ENTERTRANSIT_SHOW外,还有:


WindowManagerPolicy.java    /** Window has been added to the screen. */    public static final int TRANSIT_ENTER = 1;    /** Window has been removed from the screen. */    public static final int TRANSIT_EXIT = 2;    /** Window has been made visible. */    public static final int TRANSIT_SHOW = 3;    /** Window has been made invisible.     * TODO: Consider removal as this is unused. */    public static final int TRANSIT_HIDE = 4;    /** The "application starting" preview window is no longer needed, and will     * animate away to show the real window. */    public static final int TRANSIT_PREVIEW_DONE = 5;


接着看applyAnimationLocked函数在处理TRANSIT_ENTERTRANSIT_SHOW上的区别:


boolean applyAnimationLocked(int transit, boolean isEntrance) @WindowStateAnimator.java{//如果当前正在执行动画跟这个进入动画是同类型的,那么系统不重复执行动画。if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)|| mKeyguardGoingAwayAnimation) {if (mAnimation != null && mKeyguardGoingAwayAnimation&& transit == WindowManagerPolicy.TRANSIT_PREVIEW_DONE) {//如果tranit是 TRANSIT_PREVIEW_DONE,应用窗口已经绘制过了,那么动画类型将是app_starting_exit。applyFadeoutDuringKeyguardExitAnimation();}return true;}//当前屏幕处于可显示状态。if (mService.okToDisplay()) {//特殊窗口如状态栏、导航栏通过phoneWindowManager.java选择匹配的动画资源,这个函数直接返回的是动画资源的ID如:R.anim.dock_top_enter。int anim = mPolicy.selectAnimationLw(mWin, transit);int attr = -1;Animation a = null;if (anim != 0) {//加载指定动画资源ID的动画资源。a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;}else{//根据transit类型,获取相应的属性ID,如: com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation即是 TRANSIT_ENTER对应的属性id。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;                        break;}//加载动画资源,先有属性值attr,获取对应的动画id,然后加载指定的资源。 a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);}//设置动画,把这个动画资源a记录到WindowStateAnimator.java中的变量mAnimation中。setAnimation(a);}else{//如果当前屏幕不可见,清除动画。clearAnimation();}}

/这个函数可以根据属性id,动态获取动画资源id,这意味着可以通过这些属性自定义动画资源。


Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) @AppTransition.java{        int anim = 0;        Context context = mContext;        if (animAttr >= 0) {            AttributeCache.Entry ent = getCachedAnimations(lp);            if (ent != null) {                context = ent.context;//获取动画id。                anim = ent.array.getResourceId(animAttr, 0);            }        }        if (anim != 0) {//加载动画资源。            return AnimationUtils.loadAnimation(context, anim);        }        return null;    }


基于Activity的应用程序可以在自定义的theme中,对动画属性进行自定义,如:

./frameworks/base/packages/SystemUI/res/values/styles.xml<style name="Animation.RecentPanel">//这里使用的是系统预安装的资源,也可以自己定义。<item name="android:windowEnterAnimation">@*android:anim/grow_fade_in_from_bottom</item>        <item name="android:windowExitAnimation">@*android:anim/shrink_fade_out_from_bottom</item></style>

系统预安装的动画资源在./frameworks/base/core/res/res/anim/中,这些动画通常是xml描述的,

activity_open_enter.xml<set xmlns:android="http://schemas.android.com/apk/res/android"        android:shareInterpolator="false"        android:zAdjustment="top">    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"            android:interpolator="@interpolator/decelerate_quart"            android:fillEnabled="true"            android:fillBefore="false" android:fillAfter="true"            android:duration="200"/>    <translate android:fromYDelta="8%" android:toYDelta="0"            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"            android:interpolator="@interpolator/decelerate_quint"            android:duration="350"/></set>


2)跟apptransition有关的动画AppWindowAnimator

startactivity启动一个新的Activity,或者finishActivityLocked时,会调用WMSprepareAppTransition,同时传入transit类型,如:TRANSIT_ACTIVITY_OPENTRANSIT_ACTIVITY_CLOSE等。

相关的动画类型还有:


AppTransition.java  /** Not set up for a transition. */    public static final int TRANSIT_UNSET = -1;    /** No animation for transition. */    public static final int TRANSIT_NONE = 0;    /** A window in a new activity is being opened on top of an existing one in the same task. */    public static final int TRANSIT_ACTIVITY_OPEN = 6;    /** The window in the top-most activity is being closed to reveal the     * previous activity in the same task. */    public static final int TRANSIT_ACTIVITY_CLOSE = 7;    /** A window in a new task is being opened on top of an existing one     * in another activity's task. */    public static final int TRANSIT_TASK_OPEN = 8;    /** A window in the top-most activity is being closed to reveal the     * previous activity in a different task. */    public static final int TRANSIT_TASK_CLOSE = 9;    /** A window in an existing task is being displayed on top of an existing one     * in another activity's task. */    public static final int TRANSIT_TASK_TO_FRONT = 10;    /** A window in an existing task is being put below all other tasks. */    public static final int TRANSIT_TASK_TO_BACK = 11;    /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that     * does, effectively closing the wallpaper. */    public static final int TRANSIT_WALLPAPER_CLOSE = 12;    /** A window in a new activity that does have a wallpaper is being opened on one that didn't,     * effectively opening the wallpaper. */    public static final int TRANSIT_WALLPAPER_OPEN = 13;    /** A window in a new activity is being opened on top of an existing one, and both are on top     * of the wallpaper. */    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;    /** The window in the top-most activity is being closed to reveal the previous activity, and     * both are on top of the wallpaper. */    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;    /** A window in a new task is being opened behind an existing one in another activity's task.     * The new window will show briefly and then be gone. */    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;    /** A window in a task is being animated in-place. */    public static final int TRANSIT_TASK_IN_PLACE = 17;    /** An activity is being relaunched (e.g. due to configuration change). */    public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;    /** A task is being docked from recents. */    public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;

prepareAppTransition进一步会调用applyAnimationLocked函数。


private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,            int transit, boolean enter, boolean isVoiceInteraction)@WindowManagerService.java {if (okToDisplay()) {//加载动画资源,Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode,                    mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,                    isVoiceInteraction, freeform, atoken.mTask.mTaskId);//设置动画。atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,                        mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode());}else{atoken.mAppAnimator.clearAnimation();}}

这个loadAnimation函数跟前面WindowStateAnimator加载动画资源调用的loadAnimationAnimationUtils.java

)函数,同名不同参数。

Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,            int orientation, Rect frame, Rect displayFrame, Rect insets,            @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,            int taskId)@AppTransition.java {Animation a;//Activity是与语音交互相关的。if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN                || transit == TRANSIT_TASK_OPEN                || transit == TRANSIT_TASK_TO_FRONT)) {//由动画属性id,然后获取到动画id。a = loadAnimationRes(lp, enter                    ? com.android.internal.R.anim.voice_activity_open_enter                    : com.android.internal.R.anim.voice_activity_open_exit);}else if(mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {//mNextAppTransitionType是对应了当前app transition所属的定制类型。a = loadAnimationRes(mNextAppTransitionPackage, enter ?                    mNextAppTransitionEnter : mNextAppTransitionExit);}else{int animAttr = 0;switch (transit) {//应用程序可以通过android:activityOpenEnterAnimation,android:activityCloseEnterAnimation定制动画实现。case TRANSIT_ACTIVITY_OPEN:animAttr = enter                            ? WindowAnimation_activityOpenEnterAnimation                            : WindowAnimation_activityOpenExitAnimation;                    break;case TRANSIT_ACTIVITY_CLOSE:                    animAttr = enter                            ? WindowAnimation_activityCloseEnterAnimation                            : WindowAnimation_activityCloseExitAnimation;                   break;}//加载动画资源。a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;} return a;}

具体看下mNextAppTransitionType== NEXT_TRANSIT_TYPE_CUSTOM相关的动画定制,相应的资源idmNextAppTransitionEntermNextAppTransitionExit,是怎么设置的?

在定义一个Activity时,可以重载一个方法overridePendingTransition(intenterAnim, int exitAnim)

两个参数一个是进入动画,一个退出动画。


public void overridePendingTransition(int enterAnim, int exitAnim)@Activity.java {//调用AMS中的方法 overridePendingTransition。ActivityManagerNative.getDefault().overridePendingTransition(                    mToken, getPackageName(), enterAnim, exitAnim);}

public void overridePendingTransition(IBinder token, String packageName,            int enterAnim, int exitAnim) @ActivityManagerService.java{if (self.state == ActivityState.RESUMED                    || self.state == ActivityState.PAUSING) {//通过WMS调用AppTransition.java中的overridePendingAppTransition。                mWindowManager.overridePendingAppTransition(packageName,                        enterAnim, exitAnim, null);         }}

函数中的变量mNextAppTransitionEntermNextAppTransitionExit即是loadAnimation中用到的资源id


void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,            IRemoteCallback startedCallback) @AppTransition.java{        if (isTransitionSet()) {            clear();            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;            mNextAppTransitionPackage = packageName;            mNextAppTransitionEnter = enterAnim;            mNextAppTransitionExit = exitAnim;            postAnimationCallback();            mNextAppTransitionCallback = startedCallback;        } else {            postAnimationCallback();        }    }


最后加载的动画资源通过atoken.mAppAnimator.setAnimation(),(其中atoken类型是AppWindowToken)存储到AppWindowAnimator.java中的变量animation中。


3)前面只是动画资源的加载过程,下面看下动画是怎么执行起来的?



阅读全文
0 0
原创粉丝点击