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繼續提交執行。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容