lin20080410的专栏

从站在巨人的肩上,向成为巨人迈进... 互相学习!

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

转载两篇矢量动画的文章,做个记录:

矢量图形与矢量动画  https://blog.csdn.net/aqi00/article/details/54944816

Android-矢量动画技巧  https://blog.csdn.net/z82367825/article/details/60574053


窗口动画的加载:

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)前面只是动画资源的加载过程,下面看下动画是怎么执行起来的?



阅读更多
版权声明:笔记记录,互相学习,不足之处,欢迎指正! https://blog.csdn.net/lin20044140410/article/details/78828917
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭