赞
踩
接着前文分析Android T 远程动画显示流程其二
我们通过IRemoteAnimationRunner跨进程通信从系统进程来到了桌面进程,这里是真正动画播放的逻辑。
代码路径:frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner.Stub { public abstract void onAnimationStart(@WindowManager.TransitionOldType int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Runnable finishedCallback); @Override public final void onAnimationStart(@TransitionOldType int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { //调用自身抽象方法onAnimationStart onAnimationStart(transit, apps, wallpapers, nonApps, () -> { try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + " finished callback", e); } }); } ...... }
这里传递的参数都是前面RemoteAnimationController.goodToGo方法中获取的值。
transit
的值是TRANSIT_OLD_WALLPAPER_CLOSE
(12);
app
指的是桌面和应用的RemoteAnimationTarget;
wallpapers
壁纸的RemoteAnimationTarget;
nonApp
非APP类型的RemoteAnimationTarget;
finishedCallback
是FinishedCallback对象,这里传递的是调用了其onAnimationFinished()方法。
这方方法调用了自身抽象方法调用自身抽象方法onAnimationStart,onAnimationStart方法真正的实现在LauncherAnimationRunner类中
@TargetApi(Build.VERSION_CODES.P) public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { ...... @BinderThread public void onAnimationStart( int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, Runnable runnable) { Runnable r = () -> { //退出动画的流程,此时mAnimationResult为空,尚未进入该流程 finishExistingAnimation(); //创建AnimationResult,传递了两个runnable //() -> mAnimationResult = null,把AnimationResult对象置空 //runnable,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinished mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable); //传递从系统侧调用过来的参数创建动画 getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets, mAnimationResult); }; //根据mStartAtFrontOfQueue的值,执行线程 r if (mStartAtFrontOfQueue) { //将Runnable插入到消息队列的前面,以确保它尽快被执行 postAtFrontOfQueueAsynchronously(mHandler, r); } else { //将Runnable异步地插入到消息队列中,它将在队列中的其他消息之后执行。 postAsyncCallback(mHandler, r); } } ...... }
退出动画的流程
finishExistingAnimation();
@UiThread
private void finishExistingAnimation() {
if (mAnimationResult != null) {
mAnimationResult.finish();
mAnimationResult = null;
}
}
根据mAnimationResult是否为空执行finish方法,主要就是执行mASyncFinishRunnable
,后续会在动画退出流程中细讲。
创建AnimationResult
mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
public static final class AnimationResult {
......
private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
mSyncFinishRunnable = syncFinishRunnable;
mASyncFinishRunnable = asyncFinishRunnable;
}
......
}
AnimationResult主要用来返回当前动画播放结果,以便后续执行动画播放完成时的回调(mASyncFinishRunnable)。
() -> mAnimationResult = null
,一个把AnimationResult对象置空的Runnable,保存到mSyncFinishRunnable
中;
runnable
,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinished,保存到mASyncFinishRunnable
中。
传递从系统侧创建的参数创建动画
getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
mAnimationResult);
传递了从系统侧创建的参数,并传递了mAnimationResult
对象。这里调用的是RemoteAnimationFactory接口中的onCreateAnimation方法。
/** * Used with LauncherAnimationRunner as an interface for the runner to call back to the * implementation. */ @FunctionalInterface public interface RemoteAnimationFactory { /** * Called on the UI thread when the animation targets are received. The implementation must * call {@link AnimationResult#setAnimation} with the target animation to be run. */ void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result); ...... }
在最开始Launcher.startActivitySafely流程中,QuickstepTransitionManager.getActivityLaunchOptions方法中创建了AppLaunchAnimationRunner对象,并作为RemoteAnimationFactory对象传递到了。
mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
因此我们这里RemoteAnimationFactory的实现,就是在QuickstepTransitionManager.AppLaunchAnimationRunner中。
代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
private class AppLaunchAnimationRunner implements RemoteAnimationFactory { private final View mV; private final RunnableList mOnEndCallback; AppLaunchAnimationRunner(View v, RunnableList onEndCallback) { mV = v; mOnEndCallback = onEndCallback; } @Override public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result) { //创建AnimatorSet AnimatorSet anim = new AnimatorSet(); //判断桌面的是否已经不在前台 boolean launcherClosing = launcherIsATargetWithMode(appTargets, MODE_CLOSING); //检查是否从桌面小部件启动应用 final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView; //检查是否从最近应用列表启动应用 final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets); //决定是否跳过动画的第一帧 final boolean skipFirstFrame; if (launchingFromWidget) {//从桌面小部件启动应用的动画 composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets, wallpaperTargets, nonAppTargets, launcherClosing); addCujInstrumentation( anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET); skipFirstFrame = true; } else if (launchingFromRecents) {//从最近任务启动应用的动画 composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets, launcherClosing); addCujInstrumentation( anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS); skipFirstFrame = true; } else {//点击桌面图标启动应用的动画 composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets, launcherClosing); addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON); skipFirstFrame = false; } //桌面不在前台给动画添加一个监听器 if (launcherClosing) { anim.addListener(mForceInvisibleListener); } //设置动画和回调 result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy, skipFirstFrame); } @Override public void onAnimationCancelled() { mOnEndCallback.executeAllAndDestroy(); } }
这里我们主要关注点击桌面图标启动应用的动画逻辑
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
anim
一个AnimatorSet对象;
mV
这里指的是启动的应用图标,比如com.android.launcher3.BubbleTextView{bace738 VFED..CL. ........ 582,525-859,945 #7f09016a app:id/icon}
;
appTargets
指的是桌面和应用的RemoteAnimationTarget;
wallpaperTargets
壁纸的RemoteAnimationTarget;
nonAppTargets
非APP类型的RemoteAnimationTarget;
launcherClosing
此时桌面的是否已经不在前台,因此值为true
/** * Compose the animations for a launch from the app icon. * * @param anim the animation to add to * @param v the launching view with the icon * @param appTargets the list of opening/closing apps * @param launcherClosing true if launcher is closing */ private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTarget[] appTargets, @NonNull RemoteAnimationTarget[] wallpaperTargets, @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) { // Set the state animation first so that any state listeners are called // before our internal listeners. mLauncher.getStateManager().setCurrentAnimation(anim); // Note: the targetBounds are relative to the launcher int startDelay = getSingleFrameMs(mLauncher); Animator windowAnimator = getOpeningWindowAnimators( v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing); windowAnimator.setStartDelay(startDelay); anim.play(windowAnimator); if (launcherClosing) { // Delay animation by a frame to avoid jank. Pair<AnimatorSet, Runnable> launcherContentAnimator = getLauncherContentAnimator(true /* isAppOpening */, startDelay, false); anim.play(launcherContentAnimator.first); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { launcherContentAnimator.second.run(); } }); } }
之前最为关键的就是getOpeningWindowAnimators方法
Animator windowAnimator = getOpeningWindowAnimators(
v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);
这个方法是动画真正的设置部分
/** * @return Animator that controls the window of the opening targets from app icons. */ private Animator getOpeningWindowAnimators(View v, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) { //获取应用方向 int rotationChange = getRotationChange(appTargets); //获取启动应用的窗口边界 Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange); //检查appTargets中所有应用目标是否透明 boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets); RectF launcherIconBounds = new RectF(); //获取一个浮动图标视图 FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v, !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */); Rect crop = new Rect(); Matrix matrix = new Matrix(); //创建mMode为MODE_OPENING的RemoteAnimationTargets对象 //把app、壁纸和非app类型的RemoteAnimationTarget对象保存到RemoteAnimationTargets中 RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING); //创建SurfaceTransactionApplier对象 SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView); //为了确保动画完成时,释放相关资源 openingTargets.addReleaseCheck(surfaceApplier); //获取导航栏的RemoteAnimationTarget对象 RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget(); int[] dragLayerBounds = new int[2]; mDragLayer.getLocationOnScreen(dragLayerBounds); //检查是否支持冷启动窗口Splash Screen final boolean hasSplashScreen; if (supportsSSplashScreen()) { int taskId = openingTargets.getFirstAppTargetTaskId(); Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0); Pair<Integer, Integer> taskParams = mTaskStartParams.getOrDefault(taskId, defaultParams); mTaskStartParams.remove(taskId); hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else { hasSplashScreen = false; } //创建AnimOpenProperties对象,设置应用启动时的动画属性 AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile, windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1], hasSplashScreen, floatingView.isDifferentFromAppIcon()); //计算裁剪区域的边界 int left = prop.cropCenterXStart - prop.cropWidthStart / 2; int top = prop.cropCenterYStart - prop.cropHeightStart / 2; int right = left + prop.cropWidthStart; int bottom = top + prop.cropHeightStart; // Set the crop here so we can calculate the corner radius below. crop.set(left, top, right, bottom); //创建临时矩形和点对象 RectF floatingIconBounds = new RectF(); RectF tmpRectF = new RectF(); Point tmpPos = new Point(); //设置动画的一些参数和监听 AnimatorSet animatorSet = new AnimatorSet(); ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setDuration(APP_LAUNCH_DURATION); //设置动画的插值器为LINEAR。插值器决定了动画的速度曲线。LINEAR意味着动画将匀速进行 appAnimator.setInterpolator(LINEAR); //为appAnimator添加一个动画监听器floatingView。 //当动画开始、结束、取消或重复时,floatingView上的相应方法将被调用。 appAnimator.addListener(floatingView); appAnimator.addListener(new AnimatorListenerAdapter() { @Override //监听动开始 public void onAnimationStart(Animator animation) { //获取LauncherTaskbarUIController的实例 LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController(); //检查是否应该调用shouldShowEdu() if (taskbarController != null && taskbarController.shouldShowEdu()) { // LAUNCHER_TASKBAR_EDUCATION_SHOWING is set to true here, when the education // flow is about to start, to avoid a race condition with other components // that would show something else to the user as soon as the app is opened. //将LAUNCHER_TASKBAR_EDUCATION_SHOWING设置为true,以避免与其他组件发生竞争 Settings.Secure.putInt(mLauncher.getContentResolver(), LAUNCHER_TASKBAR_EDUCATION_SHOWING, 1); } } @Override //监听动结束 public void onAnimationEnd(Animator animation) { if (v instanceof BubbleTextView) { //我们这里v是BubbleTextView类型 //设置控件v保持按下的状态为false ((BubbleTextView) v).setStayPressed(false); } //获取LauncherTaskbarUIController的实例 LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController(); if (taskbarController != null) { //调用shouldShowEdu() taskbarController.showEdu(); } //释放所有类型的RemoteAnimationTarget对象 //包含壁纸、app和非app类型的RemoteAnimationTarget对象 openingTargets.release(); } }); //initialWindowRadius用于设置动画开始时的窗口圆角半径 //supportsRoundedCornersOnWindows(mLauncher.getResources()判断桌面是否支持窗口圆角 final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources()) ? Math.max(crop.width(), crop.height()) / 2f : 0f; //finalWindowRadius用于设置动画结束时的窗口圆角半径 //mDeviceProfile.isMultiWindowMode检查是否处于多窗口模式 //getWindowCornerRadius(mLauncher)获取桌面窗口的圆角半径 final float finalWindowRadius = mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher); //inalShadowRadius用于设置动画结束时的阴影半径 //appTargetsAreTranslucent表示应用目标是否半透明 //mMaxShadowRadius最大阴影半径值 final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius; MultiValueUpdateListener listener = new MultiValueUpdateListener() { //mDx:这个属性表示在动画过程中,X轴上的位移变化。 //它从0开始,到prop.dX结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningXInterpolator作为插值器。 FloatProp mDx = new FloatProp(0, prop.dX, 0, APP_LAUNCH_DURATION, mOpeningXInterpolator); //这个属性表示在动画过程中,Y轴上的位移变化。 //它从0开始,到prop.dY结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。 FloatProp mDy = new FloatProp(0, prop.dY, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); //mIconScaleToFitScreen:这个属性表示应用图标在屏幕上的缩放变化。 //它从prop.initialAppIconScale开始,到prop.finalAppIconScale结束, //动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。 FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale, prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); //mIconAlpha:这个属性表示应用图标的透明度变化。 //它从prop.iconAlphaStart开始,到0结束, //动画的开始延迟为APP_LAUNCH_ALPHA_START_DELAY,时长为APP_LAUNCH_ALPHA_DURATION, //使用线性插值器(LINEAR)。 FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f, APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION, LINEAR); //mWindowRadius:这个属性表示窗口圆角的半径变化。 //它从initialWindowRadius开始,到finalWindowRadius结束,动画时长为APP_LAUNCH_DURATION, //使用mOpeningInterpolator作为插值器。 FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); //mShadowRadius:这个属性表示阴影的半径变化。 //它从0开始,到finalShadowRadius结束,动画时长为APP_LAUNCH_DURATION, //使用mOpeningInterpolator作为插值器。 FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); //mCropRectCenterX、mCropRectCenterY、mCropRectWidth、mCropRectHeight //这些属性分别表示裁剪矩形的中心X坐标、中心Y坐标、宽度和高度的变化。 //它们都有各自的起始值和结束值,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。 FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0, APP_LAUNCH_DURATION, mOpeningInterpolator); //这个属性表示导航栏的淡出效果。 //它从1开始,到0结束,动画时长为ANIMATION_NAV_FADE_OUT_DURATION, //使用NAV_FADE_OUT_INTERPOLATOR作为插值器。 FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION, NAV_FADE_OUT_INTERPOLATOR); //mNavFadeIn:这个属性表示导航栏的淡入效果。它从0开始,到1结束, //动画的开始延迟为ANIMATION_DELAY_NAV_FADE_IN,时长为ANIMATION_NAV_FADE_IN_DURATION, //使用NAV_FADE_IN_INTERPOLATOR作为插值器。 FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR); //动画的更新 @Override public void onUpdate(float percent, boolean initOnly) { // Calculate the size of the scaled icon. //计算缩放图标的大小 float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value; float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value; int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2); int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2); int right = (int) (left + mCropRectWidth.value); int bottom = (int) (top + mCropRectHeight.value); crop.set(left, top, right, bottom); final int windowCropWidth = crop.width(); final int windowCropHeight = crop.height(); if (rotationChange != 0) { Utilities.rotateBounds(crop, mDeviceProfile.widthPx, mDeviceProfile.heightPx, rotationChange); } // Scale the size of the icon to match the size of the window crop. //缩放图标的大小以匹配窗口裁剪的大小。 float scaleX = iconWidth / windowCropWidth; float scaleY = iconHeight / windowCropHeight; float scale = Math.min(1f, Math.max(scaleX, scaleY)); float scaledCropWidth = windowCropWidth * scale; float scaledCropHeight = windowCropHeight * scale; float offsetX = (scaledCropWidth - iconWidth) / 2; float offsetY = (scaledCropHeight - iconHeight) / 2; // Calculate the window position to match the icon position. //计算窗口位置以匹配图标位置。 tmpRectF.set(launcherIconBounds); tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]); tmpRectF.offset(mDx.value, mDy.value); Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value); float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale; float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale; // Calculate the icon position. //计算图标位置 floatingIconBounds.set(launcherIconBounds); floatingIconBounds.offset(mDx.value, mDy.value); Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value); floatingIconBounds.left -= offsetX; floatingIconBounds.top -= offsetY; floatingIconBounds.right += offsetX; floatingIconBounds.bottom += offsetY; if (initOnly) { // For the init pass, we want full alpha since the window is not yet ready. //使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。 floatingView.update(1f, 255, floatingIconBounds, percent, 0f, mWindowRadius.value * scale, true /* isOpening */); return; } SurfaceTransaction transaction = new SurfaceTransaction(); //遍历桌面和启动应用的RemoteAnimationTarget,获取其leash,分别做处理 for (int i = appTargets.length - 1; i >= 0; i--) { RemoteAnimationTarget target = appTargets[i]; SurfaceProperties builder = transaction.forSurface(target.leash); if (target.mode == MODE_OPENING) { /** * 如果目标模式是MODE_OPENING(打开模式),代码会设置一个矩阵(matrix)来进行缩放和平移操作。 * 根据rotationChange的值(可能是表示屏幕旋转的变量),代码会决定如何平移窗口。 * 然后,使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。 * 接着,通过builder.setMatrix等方法设置窗口的矩阵、裁剪区域、透明度、圆角半径和阴影半径。 */ matrix.setScale(scale, scale); if (rotationChange == 1) { matrix.postTranslate(windowTransY0, mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth)); } else if (rotationChange == 2) { matrix.postTranslate( mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth), mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight)); } else if (rotationChange == 3) { matrix.postTranslate( mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight), windowTransX0); } else { matrix.postTranslate(windowTransX0, windowTransY0); } floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f, mWindowRadius.value * scale, true /* isOpening */); builder.setMatrix(matrix) .setWindowCrop(crop) .setAlpha(1f - mIconAlpha.value) .setCornerRadius(mWindowRadius.value) .setShadowRadius(mShadowRadius.value); } else if (target.mode == MODE_CLOSING) { /** * 如果目标模式是MODE_CLOSING(关闭模式),代码会处理关闭动画。 * 首先,根据目标的本地边界或位置设置临时位置(tmpPos)。 * 然后,根据rotationChange的值,可能需要对裁剪区域(crop)和临时位置进行旋转调整。 * 最后,设置窗口的矩阵和裁剪区域,并将透明度设置为1(完全不透明)。 */ if (target.localBounds != null) { tmpPos.set(target.localBounds.left, target.localBounds.top); } else { tmpPos.set(target.position.x, target.position.y); } final Rect crop = new Rect(target.screenSpaceBounds); crop.offsetTo(0, 0); if ((rotationChange % 2) == 1) { int tmp = crop.right; crop.right = crop.bottom; crop.bottom = tmp; tmp = tmpPos.x; tmpPos.x = tmpPos.y; tmpPos.y = tmp; } matrix.setTranslate(tmpPos.x, tmpPos.y); builder.setMatrix(matrix) .setWindowCrop(crop) .setAlpha(1f); } } /** * 如果navBarTarget不为空(即存在导航栏目标),代码会为其设置动画和视图属性。 * 根据`mNavFadeIn.value`的值,决定是淡入还是淡出导航栏。如果淡入值大于起始值,则应用淡入动画; */ if (navBarTarget != null) { SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash); if (mNavFadeIn.value > mNavFadeIn.getStartValue()) { matrix.setScale(scale, scale); matrix.postTranslate(windowTransX0, windowTransY0); navBuilder.setMatrix(matrix) .setWindowCrop(crop) .setAlpha(mNavFadeIn.value); } else { navBuilder.setAlpha(mNavFadeOut.value); } } surfaceApplier.scheduleApply(transaction); } }; appAnimator.addUpdateListener(listener); // Since we added a start delay, call update here to init the FloatingIconView properly. listener.onUpdate(0, true /* initOnly */); // If app targets are translucent, do not animate the background as it causes a visible // flicker when it resets itself at the end of its animation. if (appTargetsAreTranslucent || !launcherClosing) { animatorSet.play(appAnimator); } else { animatorSet.playTogether(appAnimator, getBackgroundAnimator()); } return animatorSet; }
回到QuickstepTransitionManager.AppLaunchAnimationRunner.onCreateAnimation方法中
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
skipFirstFrame);
代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@UiThread public void setAnimation(AnimatorSet animation, Context context) { setAnimation(animation, context, null, true); } /** * Sets the animation to play for this app launch * @param skipFirstFrame Iff true, we skip the first frame of the animation. * We set to false when skipping first frame causes jank. */ @UiThread public void setAnimation(AnimatorSet animation, Context context, @Nullable Runnable onCompleteCallback, boolean skipFirstFrame) { if (mInitialized) { throw new IllegalStateException("Animation already initialized"); } mInitialized = true; mAnimator = animation; mOnCompleteCallback = onCompleteCallback; if (mAnimator == null) { finish(); } else if (mFinished) { // Animation callback was already finished, skip the animation. mAnimator.start(); mAnimator.end(); if (mOnCompleteCallback != null) { mOnCompleteCallback.run(); } } else { // Start the animation mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finish(); } }); mAnimator.start(); if (skipFirstFrame) { // Because t=0 has the app icon in its original spot, we can skip the // first frame and have the same movement one frame earlier. mAnimator.setCurrentPlayTime( Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration())); } } } }
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) { for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) { //mSurfaceAnimationSources中每个容器,做对应的onAnimationFinished mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim); } //清除动画源列表 mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { //调用DisplayContent的onWindowAnimationFinished方法 //从当前源码上看,主要是针对输入法相关做了一些操作 mDisplayContent.onWindowAnimationFinished(this, type); } } /** * Called when an animation has finished running. */ protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) { //主要用于 清空 mSurfaceAnimationSources 列表 doAnimationFinished(type, anim); //WindowManagerService中实现onAnimationFinished() //用于唤醒所有等待mGlobalLock对象的线程,确保多个线程能够正确地执行任务 mWmService.onAnimationFinished(); //将 mNeedsZBoost 设置为 false,表示不再需要Z轴增强 mNeedsZBoost = false; }
我们这里mSurfaceAnimationSources
是保存的是需要做动画的ActivityRecord,mSurfaceAnimationSources
的值是在applyAnimationUnchecked方法中添加的。
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
调用了不同容器onAnimationFinished方法,在ActivityRecord和WindowState中都重写了这个方法。我们这里是远程动画,主要调用的就是ActivityRecord中重写的onAnimationFinished方法。
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@Override protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) { super.onAnimationFinished(type, anim); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished"); mTransit = TRANSIT_OLD_UNSET; mTransitFlags = 0; setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER, "ActivityRecord"); clearThumbnail(); setClientVisible(isVisible() || mVisibleRequested); getDisplayContent().computeImeTargetIfNeeded(this); ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s" + ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b", this, reportedVisible, okToDisplay(), okToAnimate(), isStartingWindowDisplayed()); // clean up thumbnail window if (mThumbnail != null) { mThumbnail.destroy(); mThumbnail = null; } // WindowState.onExitAnimationDone might modify the children list, so make a copy and then // traverse the copy. final ArrayList<WindowState> children = new ArrayList<>(mChildren); children.forEach(WindowState::onExitAnimationDone); // The starting window could transfer to another activity after app transition started, in // that case the latest top activity might not receive exit animation done callback if the // starting window didn't applied exit animation success. Notify animation finish to the // starting window if needed. if (task != null && startingMoved) { final WindowState transferredStarting = task.getWindow(w -> w.mAttrs.type == TYPE_APPLICATION_STARTING); if (transferredStarting != null && transferredStarting.mAnimatingExit && !transferredStarting.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) { transferredStarting.onExitAnimationDone(); } } getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token); scheduleAnimation(); // Schedule to handle the stopping and finishing activities which the animation is done // because the activities which were animating have not been stopped yet. mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。