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_STARTING给WMS,进一步调用removeStartingWindow()@PhoneWindowManager.java,处理销毁窗口相关的数据。
窗口的切换动画,总的就两类进入动画,退出动画。动画的属性有平移、旋转、缩放、透明度,这些属性可以组合使用,除了透明度是通过变换float值之外,其他三类属性都是用Matrix运算来实现的。
android中动画类型有好几种,如AppWindowAnimator,WindowAnimator,WindowStateAnimator,ScreenRotationAnimation,原理都是一样的。接下来主要关注动画资源是怎么加载的,动画是怎么执行的。
动画资源的加载。
1)跟窗口有关的动画WindowStateAnimator,当一个Activity启动时,会调用到WMS的relayoutWindow来申请窗口,这其中就应用到窗口的切换动画,如果窗口进入动画,具体就是调用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_ENTER和TRANSIT_SHOW两种,当mEnterAnimationPending为true时,程序执行TRANSIT_ENTER动画。mEnterAnimationPending的值是在WMS中设置的,一种情况是新添加窗口addWindow时:winAnimator.mEnterAnimationPending= true;还有一种情况是relayoutVisibleWindow时,可见状态从GONE到VISIBLE:
if (oldVisibility ==View.GONE)winAnimator.mEnterAnimationPending = true;
与window相关的动画类型除了TRANSIT_ENTER和TRANSIT_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_ENTER和TRANSIT_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时,会调用WMS的prepareAppTransition,同时传入transit类型,如:TRANSIT_ACTIVITY_OPEN,TRANSIT_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加载动画资源调用的loadAnimation(AnimationUtils.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相关的动画定制,相应的资源id是mNextAppTransitionEnter或mNextAppTransitionExit,是怎么设置的?
在定义一个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);
}
}
函数中的变量mNextAppTransitionEnter,mNextAppTransitionExit即是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)前面只是动画资源的加载过程,下面看下动画是怎么执行起来的?
还没有评论,来说两句吧...