一、轉場動畫
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繼續提交執行。