Android 应用转场动画RemoteAnimation

一、转场动画

android5.0之前使用overridePendingTransition,之后使用ActivityOptions和ActivityOptionsCompat可以实现新风格的转场动画。ActivityOptionsCompat是ActivityOptions的兼容包。

1.如何使用

ActivityOptions activityOptions = getActivityOptions(...);
Bundle optsBundle = activityLaunchOptions.toBundle();
context.startActivity(intent, optsBundle);

2.ActivityOptions

(1) makeCustomAnimation(Context context, int enterResId, int exitResId)
用户自定义动画,指定进入和退出动画,api16开始支持
参数说明:
enterResId:Activity进入动画资源id
exitResId:Activity退出动画资源id
(2) makeClipRevealAnimation (View source, int startX, int startY, int width, int height)
从一个view的剪切区域放大然后打开新的Activity,Api23开始支持
参数说明:
startX,startY:区域起点,利用source作为原点
width,height:区域宽高
(3) makeScaleUpAnimation(View source, int startX, int startY, int width, int height)
放大一个view,然后显示新的Activity
参数说明:
view:放大的view
startX,startY:从哪里开始缩放,以source为原点
width,height:新的activity从多大开始放大,如果是0,0则表示从最小开始。
(4) makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)
放大一张图片,然后打开activity
参数说明
source:参考原点
thumbnail:要放大的图片
startX,startY:从哪里开始放大,以source为坐标原点
(5) makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)
共享元素动画
(6) makeRemoteAnimation(RemoteAnimationAdapterCompat remoteAnimationAdapter)
应用启动和关闭自定义动画,主要Launcher和多任务使用,如:Launcher启动应用时候,先用图标做放大动销,之后用RemoteAnimation应用窗口放大动画。该方法无法直接使用,需要使用systemUi打包的 sysui_shared.jar

二、RemoteAnimation

本文已Android9.0的Launcher3为例进行讲解(可从google源码中进行下载),高版本的原理一样的。

1.使用依赖

方法无法直接使用,依赖sysui_shared.jar,该jar由SystemUI源码make进行构建,android9及以下版本可以由 google提供的Launcher 源码的中获取,见quickstep的libs下

2.Launcher3有关RemoteAnimation源码介绍

Launcher3源码中RemoteAnimation、多任务、手势相关代码都在quickstep目录下,RemoteAnimation见LauncherAppTransitionManagerImpl.java 类

 public LauncherAppTransitionManagerImpl(Context context) {
        mLauncher = Launcher.getLauncher(context);
        mDragLayer = mLauncher.getDragLayer();
        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
        mHandler = new Handler(Looper.getMainLooper());
         ...
        // 注册应用关闭监听做动画
        registerRemoteAnimations();
    }

public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
        // 权限判断
        if (hasControlRemoteAppTransitionPermission()) {
            RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
                    true /* startAtFrontOfQueue */) {
                @Override
                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
                        AnimationResult result) {
                          // 应用打开回调到这,可执行图标和窗口动画q
                           ......
                }
            };
            // 应用打开和fwk进行交互
            return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
                    runner, duration, statusBarTransitionDelay));
        }
        return super.getActivityLaunchOptions(launcher, v);
    }

    public static ActivityOptions makeRemoteAnimation(RemoteAnimationAdapterCompat remoteAnimationAdapter) {
        return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
    }


其核心是RemoteAnimationAdapterCompat类

public class RemoteAnimationAdapterCompat {
    private final RemoteAnimationAdapter mWrapped;

    public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration, long statusBarTransitionDelay) {
        this.mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration, statusBarTransitionDelay);
    }

    RemoteAnimationAdapter getWrapped() {
        return this.mWrapped;
    }

    private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
        return new IRemoteAnimationRunner.Stub() {
            public void onAnimationStart(RemoteAnimationTarget[] apps, final IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
                // 开开始的回调
                RemoteAnimationTargetCompat[] appsCompat = RemoteAnimationTargetCompat.wrap(apps);
                Runnable animationFinishedCallback = new Runnable() {
                    public void run() {
                        try {
                            // 该方法结束,动画才会结束,其打开或关闭才会进行走生命周期
                            finishedCallback.onAnimationFinished();
                        } catch (RemoteException var2) {
                            Log.e("ActivityOptionsCompat", "Failed to call app controlled animation finished callback", var2);
                        }

                    }
                };
                remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback);
            }

            public void onAnimationCancelled() throws RemoteException {
                remoteAnimationAdapter.onAnimationCancelled();
            }
        };
    }
}

其中可以看出IRemoteAnimationRunner是一个aidl 与 系统底层进交互。IRemoteAnimationRunner.Stub会回调onAnimationStart和onAnimationCancelled,onAnimationStar方法中有2个参数

  • RemoteAnimationTarget[]
    所有打开或关闭应用的窗口位置、taskId、SurfaceControl、mode等信息
  • IRemoteAnimationFinishedCallback
    该方法执行onAnimationFinished()后代表着整个动画执行完成,下一个应用才会继续走正常的生命周期,否则可卡住直到系统超时。

onAnimationStart和onAnimationCancelled最终回调给RemoteAnimationRunnerCompat接口

RemoteAnimationRunnerCompat 是一个接口,实现类 LauncherAnimationRunner

public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {

    private final Handler mHandler;
    private final boolean mStartAtFrontOfQueue;
    private AnimationResult mAnimationResult;

  
    public LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue) {
        mHandler = handler;
        // 是否执行到Handler队列的最前面
        mStartAtFrontOfQueue = startAtFrontOfQueue;
    }

    @BinderThread
    @Override
    public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
    // RemoteAnimationRunnerCompat回调到这,
        Runnable r = () -> {
            finishExistingAnimation();
            mAnimationResult = new AnimationResult(runnable);
            onCreateAnimation(targetCompats, mAnimationResult);
        };
        if (mStartAtFrontOfQueue) {
            // 插入队列的最前面,Handler第一个执行
            postAtFrontOfQueueAsynchronously(mHandler, r);
        } else {
            // 异步消息,有消息屏障时优先执行
            postAsyncCallback(mHandler, r);
        }
    }

    // 动画执行
    @UiThread
    public abstract void onCreateAnimation(
            RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);

    @UiThread
    private void finishExistingAnimation() {
        if (mAnimationResult != null) {
            mAnimationResult.finish();
            mAnimationResult = null;
        }
    }

    /**
     * Called by the system
     */
    @BinderThread
    @Override
    public void onAnimationCancelled() {
        postAsyncCallback(mHandler, this::finishExistingAnimation);
    }

    public static final class AnimationResult {
        private final Runnable mFinishRunnable;
        private AnimatorSet mAnimator;
        private boolean mFinished = false;
        private boolean mInitialized = false;

        private AnimationResult(Runnable finishRunnable) {
            mFinishRunnable = finishRunnable;
        }

        @UiThread
        private void finish() {
            if (!mFinished) {
                mFinishRunnable.run();
                mFinished = true;
            }
        }

        @UiThread
        public void setAnimation(AnimatorSet animation) {
            if (mInitialized) {
                throw new IllegalStateException("Animation already initialized");
            }
            mInitialized = true;
            mAnimator = animation;
            if (mAnimator == null) {
                finish();
            } else if (mFinished) {
                mAnimator.start();
                mAnimator.end();
            } else {
                mAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        finish();
                    }
                });
                mAnimator.start();
                mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS);
            }
        }
    }
}

3.Launcher3中应用打开动画

入口在getActivityLaunchOptions方法中的 runner

RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
                    true) {
                @Override
                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
                        AnimationResult result) {
                    AnimatorSet anim = new AnimatorSet();
                    // 判断是否launcher进行关闭(应用打开launcher关闭)
                    boolean launcherClosing =launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
                    if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
                         // 非多任务即launcher
                        mLauncher.getStateManager().setCurrentAnimation(anim);
                        // 获取目标应用的窗口位置
                        Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
                        // 执行Launcher界面上的应用图标缩放动画
                        playIconAnimators(anim, v, windowTargetBounds);
                        if (launcherClosing) {
                            // 获取Launcher自己(workspace、AllAppsContainerView)需要执行的动画
                            Pair<AnimatorSet, Runnable> launcherContentAnimator =
                                    getLauncherContentAnimator(true /* isAppOpening */);
                            anim.play(launcherContentAnimator.first);
                            anim.addListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {
                                    launcherContentAnimator.second.run();
                                }
                            });
                        }
                        // 同时执行应用的窗口动画
                        anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds));
                    }
                    if (launcherClosing) {
                        anim.addListener(mForceInvisibleListener);
                    }
                    // 设置动画可执行
                    result.setAnimation(anim);
                }
            };

由上可见,应用打开时有三个动画:图标icon动画、应用窗口动画、Launcher动画。

3.1 应用的Icon动画

 private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds) {
        final boolean isBubbleTextView = v instanceof BubbleTextView;
      // 创建一个空view
        mFloatingView = new View(mLauncher);
        if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
            // 获取点击Icon的bitmap 设置给背景
            mFloatingView.setBackground(
                    DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag()));
        }
        Rect rect = new Rect();
        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
        if (fromDeepShortcutView) {
            // 深度快捷方式视图的图标绘制在单独的视图中
            DeepShortcutView view = (DeepShortcutView) v.getParent();
            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
        } else {
           // 获取点击的Icon 在屏幕上的位置
            mDragLayer.getDescendantRectRelativeToSelf(v, rect);
        }
        int viewLocationLeft = rect.left;
        int viewLocationTop = rect.top;
        // 初始缩放比例
        float startScale = 1f;
        if (isBubbleTextView && !fromDeepShortcutView) {
            BubbleTextView btv = (BubbleTextView) v;
            btv.getIconBounds(rect);
            Drawable dr = btv.getIcon();
            if (dr instanceof FastBitmapDrawable) {
                startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
            }
        } else {
            rect.set(0, 0, rect.width(), rect.height());
        }
        viewLocationLeft += rect.left;
        viewLocationTop += rect.top;
       //mIsRtl 是否是从右向左绘制
        int viewLocationStart = mIsRtl
                ? windowTargetBounds.width() - rect.right
                : viewLocationLeft;
        // 设置FloatingView在屏幕上的位置
        LayoutParams lp = new LayoutParams(rect.width(), rect.height());
        lp.ignoreInsets = true;
        lp.setMarginStart(viewLocationStart);
        lp.topMargin = viewLocationTop;
        mFloatingView.setLayoutParams(lp);  
        mFloatingView.setLeft(viewLocationLeft);
        mFloatingView.setTop(viewLocationTop);
        mFloatingView.setRight(viewLocationLeft + rect.width());
        mFloatingView.setBottom(viewLocationTop + rect.height());
         // FloatingView添加到Launcher的根布局上
        ((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
        // 点击的应用Icon 进行隐藏
        v.setVisibility(View.INVISIBLE);

        int[] dragLayerBounds = new int[2];
        mDragLayer.getLocationOnScreen(dragLayerBounds);

        //在屏幕坐标中,将应用程序图标动画化到窗口边界的中心。
        float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
        float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];

        float xPosition = mIsRtl
                ? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
                : lp.getMarginStart();
        float dX = centerX - xPosition - (lp.width / 2);
        float dY = centerY - lp.topMargin - (lp.height / 2);

        ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
        ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
        //对位于屏幕下半部分或/相对靠近中心
        boolean useUpwardAnimation = lp.topMargin > centerY
                || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
        if (useUpwardAnimation) {
            x.setDuration(APP_LAUNCH_CURVED_DURATION);
            y.setDuration(APP_LAUNCH_DURATION);
        } else {
            x.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_DURATION));
            y.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_CURVED_DURATION));
        }
        x.setInterpolator(AGGRESSIVE_EASE);
        y.setInterpolator(AGGRESSIVE_EASE);
        // 动画设置x.y
        appOpenAnimator.play(x);
        appOpenAnimator.play(y);

        // 缩放应用程序图标以占据整个屏幕,做缩放动画
        float maxScaleX = windowTargetBounds.width() / (float) rect.width();
        float maxScaleY = windowTargetBounds.height() / (float) rect.height();
        float scale = Math.max(maxScaleX, maxScaleY);
        ObjectAnimator scaleAnim = ObjectAnimator
                .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
        scaleAnim.setDuration(APP_LAUNCH_DURATION)
                .setInterpolator(Interpolators.EXAGGERATED_EASE);
        appOpenAnimator.play(scaleAnim);

        // 应用Icon 淡出动画
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
        if (useUpwardAnimation) {
            alpha.setStartDelay(APP_LAUNCH_ALPHA_START_DELAY);
            alpha.setDuration(APP_LAUNCH_ALPHA_DURATION);
        } else {
            alpha.setStartDelay((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR
                    * APP_LAUNCH_ALPHA_START_DELAY));
            alpha.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_ALPHA_DURATION));
        }
        alpha.setInterpolator(LINEAR);
        appOpenAnimator.play(alpha);

        appOpenAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束,删除FloatingView,显示原有的应用Icon
                v.setVisibility(View.VISIBLE);
                ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView);
            }
        });
    }

总结:可以看出先对应用Icon进行隐藏,添加了一个假的View在Launcher根布局相应的位置,对假的View做移动、缩放、透明动画,动画结束后,删除假的View,显示真正的应用Icon

3.2 应用的窗口动画

 private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
            Rect windowTargetBounds) {
    // 获取应用Icon在屏幕上的位置
        Rect bounds = new Rect();
        if (v.getParent() instanceof DeepShortcutView) {
            DeepShortcutView view = (DeepShortcutView) v.getParent();
            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds);
        } else if (v instanceof BubbleTextView) {
            ((BubbleTextView) v).getIconBounds(bounds);
        } else {
            mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
        }
      // 用于获取假的icon  的位置信息
        int[] floatingViewBounds = new int[2];
        // 用于窗口动画位置信息
        Rect crop = new Rect();
        // 用于做窗口平移缩放等动画
        Matrix matrix = new Matrix();
        // 获取打开应用的RemoteAnimationTarget
        RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
                MODE_OPENING);
          // 获取关闭应用的RemoteAnimationTarget
        RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
                MODE_CLOSING);
       // 窗口动画的真正执行类
        SyncRtSurfaceTransactionApplier surfaceApplier = new SyncRtSurfaceTransactionApplier(
                mFloatingView);

        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
        appAnimator.setDuration(APP_LAUNCH_DURATION);
        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
            // 透明动画参数类封装
            FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
            @Override
            public void onUpdate(float percent) {
                final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent);
                // 计算 icon的宽、高
                float iconWidth = bounds.width() * mFloatingView.getScaleX();
                float iconHeight = bounds.height() * mFloatingView.getScaleY();
                // 计算缩放的比例
                float scaleX = iconWidth / windowTargetBounds.width();
                float scaleY = iconHeight / windowTargetBounds.height();
                float scale = Math.min(1f, Math.min(scaleX, scaleY));
                // 计算该缩放比例下的窗口宽、高
                int windowWidth = windowTargetBounds.width();
                int windowHeight = windowTargetBounds.height();
                float scaledWindowWidth = windowWidth * scale;
                float scaledWindowHeight = windowHeight * scale;
                 // 偏移      
                float offsetX = (scaledWindowWidth - iconWidth) / 2;
                float offsetY = (scaledWindowHeight - iconHeight) / 2;
                mFloatingView.getLocationOnScreen(floatingViewBounds);
                 // 窗口平移 x、y
                float transX0 = floatingViewBounds[0] - offsetX;
                float transY0 = floatingViewBounds[1] - offsetY;
                // 制作窗口裁剪的动画,使其以正方形开始,然后显示水平方向
                float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent);
                float initialTop = (windowHeight - windowWidth) / 2f;
                crop.left = 0;
                crop.top = (int) (initialTop * (1 - easePercent));
                crop.right = windowWidth;
                crop.bottom = (int) (crop.top + cropHeight);
               
                SurfaceParams[] params = new SurfaceParams[targets.length];
                for (int i = targets.length - 1; i >= 0; i--) {
                    RemoteAnimationTargetCompat target = targets[i];
                    Rect targetCrop;
                    float alpha;
                    if (target.mode == MODE_OPENING) {
                        // 打开状态的应用做缩放、平移、透明动画
                        matrix.setScale(scale, scale);
                        matrix.postTranslate(transX0, transY0);
                        targetCrop = crop;
                        alpha = mAlpha.value;
                    } else {
                      // 关闭状态的应用 动画
                        matrix.setTranslate(target.position.x, target.position.y);
                        alpha = 1f;
                        targetCrop = target.sourceContainerBounds;
                    }
                    params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
                            RemoteAnimationProvider.getLayer(target, MODE_OPENING));
                }
               //交由surfaceApplier进行真正的执行
                surfaceApplier.scheduleApply(params);
            }
        });
        return appAnimator;
    }

其动画主要是窗口从图标位置缩放到全屏同时伴随移动、透明等动画,动画参数先封装成
SurfaceParams,都交给SyncRtSurfaceTransactionApplier真正执行。

public class SyncRtSurfaceTransactionApplier {
    //Surface 
    private final Surface mTargetSurface;
    //ViewRootImpl
    private final ViewRootImpl mTargetViewRootImpl;
    private final float[] mTmpFloat9 = new float[9];

    public SyncRtSurfaceTransactionApplier(View targetView) {
        this.mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
        this.mTargetSurface = this.mTargetViewRootImpl != null ? this.mTargetViewRootImpl.mSurface : null;
    }

    public void scheduleApply(SurfaceParams... params) {
        if (this.mTargetViewRootImpl != null) {
            this.mTargetViewRootImpl.registerRtFrameCallback((frame) -> {
                if (this.mTargetSurface != null && this.mTargetSurface.isValid()) {
                    SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                    for(int i = params.length - 1; i >= 0; --i) {
                        SurfaceParams surfaceParams = params[i];
                        SurfaceControl surface = surfaceParams.surface;
                        t.deferTransactionUntilSurface(surface, this.mTargetSurface, frame);
                        applyParams(t, surfaceParams, this.mTmpFloat9);
                    }
                    t.setEarlyWakeup();
                  // 动画参数解析后最终交给SurfaceControl进行执行
                    t.apply();
                }
            });
            this.mTargetViewRootImpl.getView().invalidate();
        }
    }

    public static void applyParams(TransactionCompat t, SurfaceParams params) {
        applyParams(t.mTransaction, params, t.mTmpValues);
    }

    private static void applyParams(SurfaceControl.Transaction t, SurfaceParams params, float[] tmpFloat9) {
     // 给SurfaceControl设置相关属性
        t.setMatrix(params.surface, params.matrix, tmpFloat9);
        t.setWindowCrop(params.surface, params.windowCrop);
        t.setAlpha(params.surface, params.alpha);
        t.setLayer(params.surface, params.layer);
        t.show(params.surface);
    }

    public static class SurfaceParams {
        final SurfaceControl surface;
        final float alpha;
        final Matrix matrix;
        final Rect windowCrop;
        final int layer;

        public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix, Rect windowCrop, int layer) {
            this.surface = surface.mSurfaceControl;
            this.alpha = alpha;
            this.matrix = new Matrix(matrix);
            this.windowCrop = new Rect(windowCrop);
            this.layer = layer;
        }
    }
}

动画最终交给SurfaceControl.Transaction类执行

4.Launcher3中应用关闭动画

   private void registerRemoteAnimations() {
        // 需要先注册该方法,监听应用的关闭
        if (hasControlRemoteAppTransitionPermission()) {
            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
                    new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
        }
    }

 private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
        return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) {
            @Override
            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
                    AnimationResult result) {
                if (!mLauncher.hasBeenResumed()) {
                    //Launcher还没有onResume, post一个Runable在Handler队列尾部最后执行
                    mLauncher.setOnResumeCallback(() ->
                            postAsyncCallback(mHandler, () ->
                                    onCreateAnimation(targetCompats, result)));
                    return;
                }
                ...
                if (anim == null) {
                    anim = new AnimatorSet();
                    // 执行窗口关闭动画,其原理同打开
                    anim.play(getClosingWindowAnimators(targetCompats));

                    if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
                            || mLauncher.isForceInvisible()) {
                       
                        mLauncher.getStateManager().setCurrentAnimation(anim);
                        // 执行Launcher自己的页面动画(主要是workspace)
                        createLauncherResumeAnimation(anim);
                    }
                }
                mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
                result.setAnimation(anim);
            }
        };
    }

5.结尾

ActivityOptionsCompat.makeRemoteAnimation(…) 构建成ActivityOptions在应用startActivty传入,后应用打开会回调RemoteAnimationRunnerCompat的onCreateAnimation方法,其中会执行应用图标、窗口、Launcher页面动画;关闭需要同ActivityCompat(mLauncher).registerRemoteAnimations(definition)进行注册,后续动画回调执行同打开。应用图标动画,执行的是添加的floatView,原有图标隐藏,动画结束后删除foatView,显示原图标;窗口动画,把各种平移、缩放、透明等参数进行封装最终交给SurfaceControl.Transaction继续提交执行。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值