在 Android 系统中,系统主要是通过AppTransitionController和AppTransition这两个类是用于管理应用程序间切换动画的重要类。
AppTransitionController 是一个系统级别的类,用于管理应用程序间切换时的动画效果。其作用包括:
AppTransition 是 AppTransitionController 的一部分,负责实际执行应用切换动画。主要功能包括:
这两个类在 Android 系统中的重要性体现在以下几个方面:
AppTransition和AppTransitionController实例对象都是在DisplayContent对象的构造方法中被创建的。
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
class DisplayContent extends WindowContainer implements WindowManagerPolicy.DisplayContentInfo { DisplayContent(Display display, RootWindowContainer root) { super(root.mWindowManager); ...代码省略... mAppTransition = new AppTransition(mWmService.mContext, mWmService, this); mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier); mAppTransition.registerListenerLocked(mFixedRotationTransitionListener); mAppTransitionController = new AppTransitionController(mWmService, this); ...代码省略... } }
我们在Android 12系统源码_窗口管理(七)DisplayContent简介这篇文章有讲过,DisplayContent 用于管理屏幕,一块DisplayContent 对象实例代表一个屏幕设备,这样有多个屏幕的设备就可以创建多个DisplayContent 对象,虽然多数设备只有一个显示屏,但它们同样可以创建多个 DisplayContent 对象,如投屏的时候,可以创建一个虚拟的DisplayContent,DisplayContent会根据窗口的显示位置将窗口进行分组,隶属于同一个DisplayContent的窗口将会显示在同一个屏幕中,每一个DisplayContent都对应一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在对应的个屏幕中。DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合,而如果我们想让不同DisplayContent的窗口切换动画不同,就可以通过定制该DisplayContent对应的AppTransition和AppTransitionController来实现。
来看下AppTransition的相关的源码。
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
public class AppTransition implements Dump { static final int DEFAULT_APP_TRANSITION_DURATION = 336;//应用默认专场动画的执行时间 private final Context mContext; private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final TransitionAnimation mTransitionAnimation;//应用间的切换动画 private final int mConfigShortAnimTime; private final Interpolator mDecelerateInterpolator; private final Interpolator mThumbnailFadeInInterpolator; private final Interpolator mThumbnailFadeOutInterpolator; private final Interpolator mLinearOutSlowInInterpolator; private final Interpolator mFastOutLinearInInterpolator; private final Interpolator mFastOutSlowInInterpolator; private final int mClipRevealTranslationY; private final boolean mGridLayoutRecentsEnabled; private final boolean mLowRamRecentsEnabled; private final int mDefaultWindowAnimationStyleResId; private RemoteAnimationController mRemoteAnimationController; final Handler mHandler; AppTransition(Context context, WindowManagerService service, DisplayContent displayContent) { mContext = context; mService = service; mHandler = new Handler(service.mH.getLooper()); mDisplayContent = displayContent; mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.linear_out_slow_in); mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.fast_out_linear_in); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.fast_out_slow_in); mConfigShortAnimTime = context.getResources().getInteger( com.android.internal.R.integer.config_shortAnimTime); mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.decelerate_cubic); mThumbnailFadeInInterpolator = new Interpolator() { @Override public float getInterpolation(float input) { // Linear response for first fraction, then complete after that. if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) { return 0f; } float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION); return mFastOutLinearInInterpolator.getInterpolation(t); } }; mThumbnailFadeOutInterpolator = new Interpolator() { @Override public float getInterpolation(float input) { // Linear response for first fraction, then complete after that. if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION; return mLinearOutSlowInInterpolator.getInterpolation(t); } return 1f; } }; mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP * mContext.getResources().getDisplayMetrics().density); mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false); mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic(); final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes( com.android.internal.R.styleable.Window); mDefaultWindowAnimationStyleResId = windowStyle.getResourceId( com.android.internal.R.styleable.Window_windowAnimationStyle, 0); windowStyle.recycle(); } /** * * @param lp 窗口参数 * @param transit 窗口切换动画类型 * @param enter 是否是进入 * @param frame 如果是进入动画,则是动画结束后窗口的位置,如果是退出动画,则是动画开始时窗口的位置 * @return 返回的就是最终的窗口转换动画 */ Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode, int orientation, Rect frame, Rect displayFrame, Rect insets, @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, boolean freeform, WindowContainer container) { if (mNextAppTransitionOverrideRequested && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) { mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; } Animation a; if (isKeyguardGoingAwayTransitOld(transit) && enter) { a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags, transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER); } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { a = null; } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) { a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); } else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { a = null; } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_OPEN || transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT)) { a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_CLOSE || transit == TRANSIT_OLD_TASK_CLOSE || transit == TRANSIT_OLD_TASK_TO_BACK)) { a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) { a = mTransitionAnimation.createRelaunchAnimation(frame, insets, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s transit=%s Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { a = mTransitionAnimation.loadAppTransitionAnimation(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s " + "isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) { a = mTransitionAnimation.loadAppTransitionAnimation( mNextAppTransitionPackage, mNextAppTransitionInPlace); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE " + "transit=%s Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) { a = mTransitionAnimation.createClipRevealAnimationLockedCompat( transit, enter, frame, displayFrame, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL " + "transit=%s Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s " + "isEntrance=%s Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container); a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter, mNextAppTransitionScaleUp, frame, transit, thumbnailHeader, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b " + "Callers=%s", a, mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN", appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get( container.hashCode()); a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter, mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b " + "Callers=%s", a, mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN", appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) { a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: " + "anim=%s transit=%s isEntrance=true Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (isChangeTransitOld(transit)) { // In the absence of a specific adapter, we just want to keep everything stationary. a = new AlphaAnimation(1.f, 1.f); a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else { int animAttr = 0; switch (transit) { case TRANSIT_OLD_ACTIVITY_OPEN: case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN: animAttr = enter ? WindowAnimation_activityOpenEnterAnimation : WindowAnimation_activityOpenExitAnimation; break; case TRANSIT_OLD_ACTIVITY_CLOSE: case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE: animAttr = enter ? WindowAnimation_activityCloseEnterAnimation : WindowAnimation_activityCloseExitAnimation; break; case TRANSIT_OLD_TASK_OPEN: animAttr = enter ? WindowAnimation_taskOpenEnterAnimation : WindowAnimation_taskOpenExitAnimation; break; case TRANSIT_OLD_TASK_CLOSE: animAttr = enter ? WindowAnimation_taskCloseEnterAnimation : WindowAnimation_taskCloseExitAnimation; break; case TRANSIT_OLD_TASK_TO_FRONT: animAttr = enter ? WindowAnimation_taskToFrontEnterAnimation : WindowAnimation_taskToFrontExitAnimation; break; case TRANSIT_OLD_TASK_TO_BACK: animAttr = enter ? WindowAnimation_taskToBackEnterAnimation : WindowAnimation_taskToBackExitAnimation; break; case TRANSIT_OLD_WALLPAPER_OPEN: animAttr = enter ? WindowAnimation_wallpaperOpenEnterAnimation : WindowAnimation_wallpaperOpenExitAnimation; break; case TRANSIT_OLD_WALLPAPER_CLOSE: animAttr = enter ? WindowAnimation_wallpaperCloseEnterAnimation : WindowAnimation_wallpaperCloseExitAnimation; break; case TRANSIT_OLD_WALLPAPER_INTRA_OPEN: animAttr = enter ? WindowAnimation_wallpaperIntraOpenEnterAnimation : WindowAnimation_wallpaperIntraOpenExitAnimation; break; case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE: animAttr = enter ? WindowAnimation_wallpaperIntraCloseEnterAnimation : WindowAnimation_wallpaperIntraCloseExitAnimation; break; case TRANSIT_OLD_TASK_OPEN_BEHIND: animAttr = enter ? WindowAnimation_launchTaskBehindSourceAnimation : WindowAnimation_launchTaskBehindTargetAnimation; break; // TODO(b/189386466): Use activity transition as the fallback. Investigate if we // need new TaskFragment transition. case TRANSIT_OLD_TASK_FRAGMENT_OPEN: animAttr = enter ? WindowAnimation_activityOpenEnterAnimation : WindowAnimation_activityOpenExitAnimation; break; // TODO(b/189386466): Use activity transition as the fallback. Investigate if we // need new TaskFragment transition. case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: animAttr = enter ? WindowAnimation_activityCloseEnterAnimation : WindowAnimation_activityCloseExitAnimation; break; } //动画 a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null; ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b " + "Callers=%s", a, animAttr, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } setAppTransitionFinishedCallbackIfNeeded(a); return a; } /** * * @param lp 窗口参数 * @param animAttr 动画资源id * @param transit 窗口切换动画类型 * @return Animation动画 */ @Nullable Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit); } }
frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java
public class TransitionAnimation { @Nullable public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { int resId = Resources.ID_NULL; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations(lp); if (ent != null) { context = ent.context; resId = ent.array.getResourceId(animAttr, 0); } } resId = updateToTranslucentAnimIfNeeded(resId, transit); if (ResourceId.isValid(resId)) { return loadAnimationSafely(context, resId, mTag); } return null; } private static int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) { if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN && anim == R.anim.activity_open_enter) { return R.anim.activity_translucent_open_enter; } if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE && anim == R.anim.activity_close_exit) { return R.anim.activity_translucent_close_exit; } return anim; } }
上一篇:数据结构:栈
下一篇:百度浏览器翻译功能在哪