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

ゝ一世哀愁。 2022-06-03 03:55 597阅读 0赞

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

矢量图形与矢量动画 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相关的操作,还处理动画流程的跟踪。

  1. void applyEnterAnimationLocked() @WindowStateAnimator.java{
  2. final int transit;
  3. if (mEnterAnimationPending) {
  4. mEnterAnimationPending = false;
  5. transit = WindowManagerPolicy.TRANSIT_ENTER;
  6. }else{
  7. transit = WindowManagerPolicy.TRANSIT_SHOW;
  8. }
  9. applyAnimationLocked(transit, true);
  10. }

从上面的函数中,进入动画分为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外,还有:

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

接着看applyAnimationLocked函数在处理TRANSIT_ENTER和TRANSIT_SHOW上的区别:

  1. boolean applyAnimationLocked(int transit, boolean isEntrance) @WindowStateAnimator.java{
  2. //如果当前正在执行动画跟这个进入动画是同类型的,那么系统不重复执行动画。
  3. if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)|| mKeyguardGoingAwayAnimation) {
  4. if (mAnimation != null && mKeyguardGoingAwayAnimation
  5. && transit == WindowManagerPolicy.TRANSIT_PREVIEW_DONE) {
  6. //如果tranit是 TRANSIT_PREVIEW_DONE,应用窗口已经绘制过了,那么动画类型将是app_starting_exit。
  7. applyFadeoutDuringKeyguardExitAnimation();
  8. }
  9. return true;
  10. }
  11. //当前屏幕处于可显示状态。
  12. if (mService.okToDisplay()) {
  13. //特殊窗口如状态栏、导航栏通过phoneWindowManager.java选择匹配的动画资源,这个函数直接返回的是动画资源的ID如:R.anim.dock_top_enter。
  14. int anim = mPolicy.selectAnimationLw(mWin, transit);
  15. int attr = -1;
  16. Animation a = null;
  17. if (anim != 0) {
  18. //加载指定动画资源ID的动画资源。
  19. a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
  20. }else{
  21. //根据transit类型,获取相应的属性ID,如: com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation即是 TRANSIT_ENTER对应的属性id。
  22. switch (transit) {
  23. case WindowManagerPolicy.TRANSIT_ENTER:
  24. attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
  25. break;
  26. case WindowManagerPolicy.TRANSIT_EXIT:
  27. attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
  28. break;
  29. case WindowManagerPolicy.TRANSIT_SHOW:
  30. attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
  31. break;
  32. }
  33. //加载动画资源,先有属性值attr,获取对应的动画id,然后加载指定的资源。
  34. a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
  35. }
  36. //设置动画,把这个动画资源a记录到WindowStateAnimator.java中的变量mAnimation中。
  37. setAnimation(a);
  38. }else{
  39. //如果当前屏幕不可见,清除动画。
  40. clearAnimation();
  41. }
  42. }

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

  1. Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) @AppTransition.java{
  2. int anim = 0;
  3. Context context = mContext;
  4. if (animAttr >= 0) {
  5. AttributeCache.Entry ent = getCachedAnimations(lp);
  6. if (ent != null) {
  7. context = ent.context;
  8. //获取动画id。
  9. anim = ent.array.getResourceId(animAttr, 0);
  10. }
  11. }
  12. if (anim != 0) {
  13. //加载动画资源。
  14. return AnimationUtils.loadAnimation(context, anim);
  15. }
  16. return null;
  17. }

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

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

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

  1. activity_open_enter.xml
  2. <set xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:shareInterpolator="false"
  4. android:zAdjustment="top">
  5. <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
  6. android:interpolator="@interpolator/decelerate_quart"
  7. android:fillEnabled="true"
  8. android:fillBefore="false" android:fillAfter="true"
  9. android:duration="200"/>
  10. <translate android:fromYDelta="8%" android:toYDelta="0"
  11. android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
  12. android:interpolator="@interpolator/decelerate_quint"
  13. android:duration="350"/>
  14. </set>

2)跟apptransition有关的动画AppWindowAnimator。

当startactivity启动一个新的Activity,或者finishActivityLocked时,会调用WMS的prepareAppTransition,同时传入transit类型,如:TRANSIT_ACTIVITY_OPEN,TRANSIT_ACTIVITY_CLOSE等。

相关的动画类型还有:

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

prepareAppTransition进一步会调用applyAnimationLocked函数。

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

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

)函数,同名不同参数。

  1. Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
  2. int orientation, Rect frame, Rect displayFrame, Rect insets,
  3. @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
  4. int taskId)@AppTransition.java {
  5. Animation a;
  6. //Activity是与语音交互相关的。
  7. if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
  8. || transit == TRANSIT_TASK_OPEN
  9. || transit == TRANSIT_TASK_TO_FRONT)) {
  10. //由动画属性id,然后获取到动画id。
  11. a = loadAnimationRes(lp, enter
  12. ? com.android.internal.R.anim.voice_activity_open_enter
  13. : com.android.internal.R.anim.voice_activity_open_exit);
  14. }else if(mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
  15. //mNextAppTransitionType是对应了当前app transition所属的定制类型。
  16. a = loadAnimationRes(mNextAppTransitionPackage, enter ?
  17. mNextAppTransitionEnter : mNextAppTransitionExit);
  18. }else{
  19. int animAttr = 0;
  20. switch (transit) {
  21. //应用程序可以通过android:activityOpenEnterAnimation,android:activityCloseEnterAnimation定制动画实现。
  22. case TRANSIT_ACTIVITY_OPEN:
  23. animAttr = enter
  24. ? WindowAnimation_activityOpenEnterAnimation
  25. : WindowAnimation_activityOpenExitAnimation;
  26. break;
  27. case TRANSIT_ACTIVITY_CLOSE:
  28. animAttr = enter
  29. ? WindowAnimation_activityCloseEnterAnimation
  30. : WindowAnimation_activityCloseExitAnimation;
  31. break;
  32. }
  33. //加载动画资源。
  34. a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
  35. }
  36. return a;
  37. }

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

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

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

  1. public void overridePendingTransition(int enterAnim, int exitAnim)@Activity.java {
  2. //调用AMS中的方法 overridePendingTransition。
  3. ActivityManagerNative.getDefault().overridePendingTransition(
  4. mToken, getPackageName(), enterAnim, exitAnim);
  5. }
  6. public void overridePendingTransition(IBinder token, String packageName,
  7. int enterAnim, int exitAnim) @ActivityManagerService.java{
  8. if (self.state == ActivityState.RESUMED
  9. || self.state == ActivityState.PAUSING) {
  10. //通过WMS调用AppTransition.java中的overridePendingAppTransition。
  11. mWindowManager.overridePendingAppTransition(packageName,
  12. enterAnim, exitAnim, null);
  13. }
  14. }

函数中的变量mNextAppTransitionEnter,mNextAppTransitionExit即是loadAnimation中用到的资源id。

  1. void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
  2. IRemoteCallback startedCallback) @AppTransition.java{
  3. if (isTransitionSet()) {
  4. clear();
  5. mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
  6. mNextAppTransitionPackage = packageName;
  7. mNextAppTransitionEnter = enterAnim;
  8. mNextAppTransitionExit = exitAnim;
  9. postAnimationCallback();
  10. mNextAppTransitionCallback = startedCallback;
  11. } else {
  12. postAnimationCallback();
  13. }
  14. }

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

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

发表评论

表情:
评论列表 (有 0 条评论,597人围观)

还没有评论,来说两句吧...

相关阅读