背景
前段时间写了一篇Android 仿微信朋友圈图片拖拽返回,有朋友指出为什么在拖拽的时候,发现上一个页面点击的图片是空白的,可以看下效果图。
出现问题的本能反应,先对比下微信朋友圈的效果,发现没问题。[手动黑人问号脸]
后来无意中发现,当手机休眠唤醒之后,这个问题就没有了。那就说明在onResume中的部分代码对view做了处理。
onResume分析
既然发现onResume是没问题的,所以进入该方法。(注意,全篇都是基于API-28分析)
//Activity.class
protected void onResume() {
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
getAutofillManager().notifyViewEntered(focus);
}
}
}
mCalled = true;
}
因为是在共享元素发现的问题,那就很明显,主要就看mActivityTransitionState.onResume(this, isTopOfTask());这行代码。继续追踪
//ActivityTransitionState.class
public void onResume(Activity activity, boolean isTopOfTask) {
if (isTopOfTask || mEnterTransitionCoordinator == null) {
restoreExitedViews();
restoreReenteringViews();
} else {
activity.mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mEnterTransitionCoordinator == null ||
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
restoreExitedViews();
restoreReenteringViews();
}
}
}, 1000);
}
}
其实就两个关键方法,restoreExitedViews();和restoreReenteringViews(); 那就分别看下这两个方法做了什么。
restoreExitedViews,字面翻译意思就是,恢复已经退出的view。
//ActivityTransitionState.class
private void restoreExitedViews() {
if (mCalledExitCoordinator != null) {
mCalledExitCoordinator.resetViews();
mCalledExitCoordinator = null;
}
}
很简单,只有一行有效代码,继续跟进,进入ExitTransitionCoordinator.class。
//ExitTransitionCoordinator.class
public void resetViews() {
ViewGroup decorView = getDecor();
if (decorView != null) {
//如果decorView不为空,则强制将动画结束
TransitionManager.endTransitions(decorView);
}
if (mTransitioningViews != null) {
//如果有过渡动画的view的话,就开始显示view
showViews(mTransitioningViews, true);
setTransitioningViewsVisiblity(View.VISIBLE, true);
}
showViews(mSharedElements, true);
mIsHidden = true;
if (!mIsReturning && decorView != null) {
decorView.suppressLayout(false);
}
//从遮罩层上移除共享元素
moveSharedElementsFromOverlay();
//清除状态
clearState();
}
关键方法1.showViews,2.setTransitioningViewsVisiblity,3.moveSharedElementsFromOverlay。其实看字面意思就是重新显示view。不妨在跟踪看下。
进入父类ActivityTransitionCoordinator.class
//ActivityTransitionCoordinator.class
protected void showViews(ArrayList<View> views, boolean setTransitionAlpha) {
int count = views.size();
for (int i = 0; i < count; i++) {
showView(views.get(i), setTransitionAlpha);
}
}
private void showView(View view, boolean setTransitionAlpha) {
Float alpha = mOriginalAlphas.remove(view);
if (alpha != null) {
view.setAlpha(alpha);
}
if (setTransitionAlpha) {
view.setTransitionAlpha(1f);
}
}
本以为这个方法主要做的是setVisibility,结果发现主要做了alpha的处理。再看setTransitioningViewsVisiblity
//ActivityTransitionCoordinator.class
protected void setTransitioningViewsVisiblity(int visiblity, boolean invalidate) {
final int numElements = mTransitioningViews == null ? 0 : mTransitioningViews.size();
for (int i = 0; i < numElements; i++) {
final View view = mTransitioningViews.get(i);
if (invalidate) {
view.setVisibility(visiblity);
} else {
view.setTransitionVisibility(visiblity);
}
}
}
终于,找到setVisibility方法了。最后看moveSharedElementsFromOverlay方法
//ActivityTransitionCoordinator.class
protected void moveSharedElementsFromOverlay() {
int numListeners = mGhostViewListeners.size();
for (int i = 0; i < numListeners; i++) {
GhostViewListeners listener = mGhostViewListeners.get(i);
listener.removeListener();
}
mGhostViewListeners.clear();
****省略部分代码****
ViewGroup decor = getDecor();
if (decor != null) {
ViewGroupOverlay overlay = decor.getOverlay();
int count = mSharedElements.size();
for (int i = 0; i < count; i++) {
//移除共享元素的view
View sharedElement = mSharedElements.get(i);
GhostView.removeGhost(sharedElement);
}
}
}
所以,showViews 将view的alpha设为1,即不透明,
setTransitioningViewsVisiblity 将view重新setVisiblity,
moveSharedElementsFromOverlay 从遮罩层中移除共享元素
其实到这里就已经知道,为什么onResume之后就没问题了。因为view setVisiblity 和 setAlpha了。但是还是看下restoreReenteringViews方法。
//ActivityTransitionState.class
private void restoreReenteringViews() {
if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() &&
!mEnterTransitionCoordinator.isCrossTask()) {
//关键代码
mEnterTransitionCoordinator.forceViewsToAppear();
mExitingFrom = null;
mExitingTo = null;
mExitingToView = null;
}
}
继续进入关键代码forceViewsToAppear
//EnterTransitionCoordinator.class
public void forceViewsToAppear() {
****省略部分代码****
if (!mIsReadyForTransition) {
mIsReadyForTransition = true;
****省略部分代码****
showViews(mTransitioningViews, true);
setTransitioningViewsVisiblity(View.VISIBLE, true);
mSharedElements.clear();
mAllSharedElementNames.clear();
mTransitioningViews.clear();
mIsReadyForTransition = true;
viewsTransitionComplete();
sharedElementTransitionComplete();
} else {
if (!mSharedElementTransitionStarted) {
moveSharedElementsFromOverlay();
mSharedElementTransitionStarted = true;
showViews(mSharedElements, true);
mSharedElements.clear();
sharedElementTransitionComplete();
}
if (!mIsViewsTransitionStarted) {
mIsViewsTransitionStarted = true;
showViews(mTransitioningViews, true);
setTransitioningViewsVisiblity(View.VISIBLE, true);
mTransitioningViews.clear();
viewsTransitionComplete();
}
cancelPendingTransitions();
}
****省略部分代码****
}
其实会发现,和上面分析的restoreExitedViews方法差不多,基本都已经分析过了。所以,这个问题到目前为止,其实已经算结束了。只要在共享元素动画结束之后,再手动的调一次onResume方法即可。但是,onResume方法太重了,还没见过解决方法用的这么重的方法的。那就继续分析吧。
共享元素分析
略长,对源码分析不感兴趣的,可以直接滑到最后。
首先,共享元素的动画监听,可以通过setExitSharedElementCallback 和 setEnterSharedElementCallback监听。其中共有7个方法,如下
//动画开始
void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)
//动画结束
void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)
//被移除的共享元素,即不需要进行共享元素动画的view
void onRejectSharedElements(List<View> rejectedSharedElements)
//共享元素view和name的键值对
void onMapSharedElements(List<String> names, Map<String, View> sharedElements)
//将view的信息以parcelable的对象类型保存,用于activity之间传递
Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds)
//将parcelable对象重新转成view
View onCreateSnapshotView(Context context, Parcelable snapshot)
//共享元素已经拿到
void onSharedElementsArrived(List<String> sharedElementNames, List<View> sharedElements, OnSharedElementsReadyListener listener)
我们不妨先打印下log,看下调用顺序。
注意:A进入B的过程,A使用setExitSharedElementCallback退出监听,B使用setEnterSharedElementCallback进入监听。
结果如下
A进入B
A:
onMapSharedElements
onCaptureSharedElementSnapshot
onSharedElementsArrived
B:
onMapSharedElements
onSharedElementsArrived
onRejectSharedElements
onCreateSnapshotView
onSharedElementStart
onSharedElementEnd
B返回到A
B:
onMapSharedElements
A:
onMapSharedElements
onCaptureSharedElementSnapshot
B:
onCreateSnapshotView
onSharedElementEnd//没看错,先end后start
onSharedElementStart
onSharedElementsArrived
A:
onSharedElementsArrived
onRejectSharedElements
onCreateSnapshotView
onSharedElementStart
onSharedElementEnd
从页面开始分析
//MainActivity.class
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, v, "share_photo");
startActivity(intent, compat.toBundle());
先从makeSceneTransitionAnimation开始,一步步跳转,进入ActivityOptions.class
//ActivityOptions.class
static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
ActivityOptions opts, SharedElementCallback callback,
Pair<View, String>[] sharedElements) {
****省略部分代码****
opts.mAnimationType = ANIM_SCENE_TRANSITION;
ArrayList<String> names = new ArrayList<String>();
ArrayList<View> views = new ArrayList<View>();
if (sharedElements != null) {
//将view和name以键值对的形式保存
for (int i = 0; i < sharedElements.length; i++) {
Pair<View, String> sharedElement = sharedElements[i];
String sharedElementName = sharedElement.second;
if (sharedElementName == null) {
throw new IllegalArgumentException("Shared element name must not be null");
}
names.add(sharedElementName);
View view = sharedElement.first;
if (view == null) {
throw new IllegalArgumentException("Shared element must not be null");
}
views.add(sharedElement.first);
}
}
//创建ExitTransitionCoordinator
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
callback, names, names, views, false);
****省略部分代码****
return exit;
}
主要就是将view和name以键值对的形式保存,并且创建了ExitTransitionCoordinator对象,进入此对象
//ExitTransitionCoordinator.class
public ExitTransitionCoordinator(Activity activity, Window window,
SharedElementCallback listener, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
super(window, names, listener, isReturning);
viewsReady(mapSharedElements(accepted, mapped));
stripOffscreenViews();
mIsBackgroundReady = !isReturning;
mActivity = activity;
}
在进入父类的viewsReady
//ActivityTransitionCoordinator.class
protected void viewsReady(ArrayMap<String, View> sharedElements) {
sharedElements.retainAll(mAllSharedElementNames);
if (mListener != null) {
mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
}
setSharedElements(sharedElements);
****省略部分代码****
}
成功找到第一个回调onMapSharedElements。所以ActivityOptionsCompat.makeSceneTransitionAnimation,主要就是将view和对应的name保存到ExitTransitionCoordinator对象中,期间会执行onMapSharedElements回调。
然后分析startActivity-->startActivityForResult-->cancelInputsAndStartExitTransition-->startExitOutTransition-->startExit
//ExitTransitionCoordinator.class
public void startExit() {
if (!mIsExitStarted) {
****省略部分代码****
moveSharedElementsToOverlay();
startTransition(new Runnable() {
@Override
public void run() {
if (mActivity != null) {
beginTransitions();
} else {
startExitTransition();
}
}
});
}
}
主要就是两个方法,1.moveSharedElementsToOverlay,2.startTransition。方法1理解字面意思即可。主要还是方法2。
因为mActivity不为null,所以直接分析beginTransitions
//ExitTransitionCoordinator.class
private void beginTransitions() {
//获取共享元素的过渡效果
Transition sharedElementTransition = getSharedElementExitTransition();
//获取其他view的过渡效果
Transition viewsTransition = getExitTransition();
//合并
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
ViewGroup decorView = getDecor();
if (transition != null && decorView != null) {
setGhostVisibility(View.INVISIBLE);
scheduleGhostVisibilityChange(View.INVISIBLE);
if (viewsTransition != null) {
setTransitioningViewsVisiblity(View.VISIBLE, false);
}
//开始过渡效果
TransitionManager.beginDelayedTransition(decorView, transition);
scheduleGhostVisibilityChange(View.VISIBLE);
setGhostVisibility(View.VISIBLE);
if (viewsTransition != null) {
setTransitioningViewsVisiblity(View.INVISIBLE, false);
}
decorView.invalidate();
} else {
transitionStarted();
}
}
主要是transition的监听
//ExitTransitionCoordinator.class
private Transition getSharedElementExitTransition() {
Transition sharedElementTransition = null;
if (!mSharedElements.isEmpty()) {
sharedElementTransition = configureTransition(getSharedElementTransition(), false);
}
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
} else {
sharedElementTransition.addListener(new ContinueTransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
//end的操作
sharedElementTransitionComplete();
if (mIsHidden) {
showViews(mSharedElements, true);
}
super.onTransitionEnd(transition);
}
});
mSharedElements.get(0).invalidate();
}
return sharedElementTransition;
}
这里会进入重写的sharedElementTransitionComplete,
//ExitTransitionCoordinator.class
@Override
protected void sharedElementTransitionComplete() {
mSharedElementBundle = mExitSharedElementBundle == null
? captureSharedElementState() : captureExitSharedElementsState();
super.sharedElementTransitionComplete();
}
此处会进入captureSharedElementState 方法以及super.sharedElementTransitionComplete();方法。
先进入父类的captureSharedElementState方法,
//ActivityTransitionCoordinator.class
protected void captureSharedElementState(View view, String name, Bundle transitionArgs,
Matrix tempMatrix, RectF tempBounds) {
****省略部分代码****
if (mListener != null) {
bitmap = mListener.onCaptureSharedElementSnapshot(view, tempMatrix, tempBounds);
}
****省略部分代码****
}
从而找到了mListener.onCaptureSharedElementSnapshot(view, tempMatrix, tempBounds)回调。
同时super.sharedElementTransitionComplete();会进入startInputWhenTransitionsComplete,再进入onTransitionsComplete,最后进入notifyComplete方法
//ExitTransitionCoordinator.class
protected void notifyComplete() {
if (isReadyToNotify()) {
if (!mSharedElementNotified) {
mSharedElementNotified = true;
delayCancel();
if (mListener == null) {
mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
notifyExitComplete();
} else {
final ResultReceiver resultReceiver = mResultReceiver;
final Bundle sharedElementBundle = mSharedElementBundle;
mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements,
new OnSharedElementsReadyListener() {
@Override
public void onSharedElementsReady() {
resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS,
sharedElementBundle);
notifyExitComplete();
}
});
}
} else {
notifyExitComplete();
}
}
}
但是因为此处isReadyToNotify()为false,所以这个方法等于没有执行。所以到目前为止,A页面已经没法继续分析了。
开始分析页面B。页面B会经过onCreate,onStart等方法。找到performStart方法,再找到mActivityTransitionState.enterReady(this); 进入enterReady
//ActivityTransitionState.class
public void enterReady(Activity activity) {
****省略部分代码****
ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
if (mEnterActivityOptions.isReturning()) {
//这个方法很熟悉,在onResume中分析过
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
//创建了EnterTransitionCoordinator对象
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
mEnterActivityOptions.isCrossTask());
if (mEnterActivityOptions.isCrossTask()) {
mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
}
if (!mIsEnterPostponed) {
startEnter();
}
}
发现创建了EnterTransitionCoordinator对象,最后调用了startEnter方法。
先分析EnterTransitionCoordinator对象。进入构造方法
//EnterTransitionCoordinator.class
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
super(activity.getWindow(), sharedElementNames,
getListener(activity, isReturning && !isCrossTask), isReturning);
mActivity = activity;
mIsCrossTask = isCrossTask;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
****省略部分代码****
}
主要看mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);方法,发送的消息,接收者就是ExitTransitionCoordinator。
//ExitTransitionCoordinator.class
case MSG_SET_REMOTE_RECEIVER:
stopCancel();
mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
if (mIsCanceled) {
mResultReceiver.send(MSG_CANCEL, null);
mResultReceiver = null;
} else {
notifyComplete();
}
break;
此处会执行notifyComplete,同时notifyComplete方法中的isReadyToNotify()为true,所以此处就会进入onSharedElementsArrived回调。到目前为止,A页面的回调已经全部找到。
//ExitTransitionCoordinator.class
protected boolean isReadyToNotify() {
return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}
isReadyToNotify方法中mResultReceiver这个对象,只有在页面B发送MSG_SET_REMOTE_RECEIVER这个消息之后,才会赋值。
最后notifyComplete方法中会有这么一行代码resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, sharedElementBundle); 内部通过handler发送了一个消息,发送给谁?我猜应该是对应的enter类吧。果然在EnterTransitionCoordinator类中搜到了。
//EnterTransitionCoordinator.class
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
case MSG_TAKE_SHARED_ELEMENTS:
if (!mIsCanceled) {
mSharedElementsBundle = resultData;
onTakeSharedElements();
}
break;
****省略部分代码****
}
}
对mSharedElementsBundle赋值,然后调用onTakeSharedElements方法。到这里先暂停下。
接着上面的startEnter方法,然后调用namedViewsReady方法,再调用triggerViewsReady方法。
//EnterTransitionCoordinator.class
private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
****省略部分代码****
if (decor == null || (decor.isAttachedToWindow() &&
(sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
viewsReady(sharedElements);
} else {
mViewsReadyListener = OneShotPreDrawListener.add(decor, () -> {
mViewsReadyListener = null;
viewsReady(sharedElements);
});
decor.invalidate();
}
}
然后进入复写的viewsReady方法
//EnterTransitionCoordinator.class
@Override
protected void viewsReady(ArrayMap<String, View> sharedElements) {
super.viewsReady(sharedElements);
mIsReadyForTransition = true;
hideViews(mSharedElements);
Transition viewsTransition = getViewsTransition();
if (viewsTransition != null && mTransitioningViews != null) {
removeExcludedViews(viewsTransition, mTransitioningViews);
stripOffscreenViews();
hideViews(mTransitioningViews);
}
if (mIsReturning) {
sendSharedElementDestination();
} else {
moveSharedElementsToOverlay();
}
if (mSharedElementsBundle != null) {
onTakeSharedElements();
}
}
此处有两个注意点,1.super.viewsReady(sharedElements); 2.最后的onTakeSharedElements();
父类的viewsReady,上面已经有分析,会回调onMapSharedElements方法,因为是EnterTransitionCoordinator类,所以是页面B的回调。
重点是onTakeSharedElements方法。首先,刚才暂停的一段代码,也是这个方法。在当前类EnterTransitionCoordinator中搜索此方法,总共就这两处调用。但是这里调用前提是mSharedElementsBundle != null,而mSharedElementsBundle只有在前面的地方赋值,重复粘贴一遍代码,如下
//EnterTransitionCoordinator.class
case MSG_TAKE_SHARED_ELEMENTS:
if (!mIsCanceled) {
mSharedElementsBundle = resultData;
onTakeSharedElements();
}
break;
所以,真正执行onTakeSharedElements方法的,是在收到MSG_TAKE_SHARED_ELEMENTS消息之后。继续分析onTakeSharedElements
//EnterTransitionCoordinator.class
private void onTakeSharedElements() {
****省略部分代码****
OnSharedElementsReadyListener listener = new OnSharedElementsReadyListener() {
@Override
public void onSharedElementsReady() {
final View decorView = getDecor();
if (decorView != null) {
OneShotPreDrawListener.add(decorView, false, () -> {
startTransition(() -> {
startSharedElementTransition(sharedElementState);
});
});
decorView.invalidate();
}
}
};
if (mListener == null) {
listener.onSharedElementsReady();
} else {
mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
}
}
这里调用了B页面的onSharedElementsArrived。同时监听的回调中调用了startSharedElementTransition方法。
//EnterTransitionCoordinator.class
private void startSharedElementTransition(Bundle sharedElementState) {
****省略部分代码****
ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
rejectedNames.removeAll(mSharedElementNames);
//创建非共享元素的view
ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
if (mListener != null) {
//回调onRejectSharedElements
mListener.onRejectSharedElements(rejectedSnapshots);
}
removeNullViews(rejectedSnapshots);
//执行非共享元素的动画
startRejectedAnimations(rejectedSnapshots);
// 创建共享元素的view,此处会回调页面B的onCreateSnapshotView方法
ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
mSharedElementNames);
showViews(mSharedElements, true);
//此处会回调页面B的onSharedElementEnd方法
scheduleSetSharedElementEnd(sharedElementSnapshots);
//此处会回调页面B的onSharedElementStart方法
ArrayList<SharedElementOriginalState> originalImageViewState =
setSharedElementState(sharedElementState, sharedElementSnapshots);
requestLayoutForSharedElements();
boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
boolean startSharedElementTransition = true;
setGhostVisibility(View.INVISIBLE);
scheduleGhostVisibilityChange(View.INVISIBLE);
pauseInput();
Transition transition = beginTransition(decorView, startEnterTransition,
startSharedElementTransition);
scheduleGhostVisibilityChange(View.VISIBLE);
setGhostVisibility(View.VISIBLE);
//开始Transition
if (startEnterTransition) {
startEnterTransition(transition);
}
setOriginalSharedElementState(mSharedElements, originalImageViewState);
if (mResultReceiver != null) {
decorView.postOnAnimation(new Runnable() {
int mAnimations;
@Override
public void run() {
if (mAnimations++ < MIN_ANIMATION_FRAMES) {
View decorView = getDecor();
if (decorView != null) {
decorView.postOnAnimation(this);
}
} else if (mResultReceiver != null) {
mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
mResultReceiver = null; // all done sending messages.
}
}
});
}
}
此处就会调用页面B的其他方法。已经在上方代码中备注。目前为止页面A进入页面B,已经大概的过了一遍。
坑啊,都N久了,还没给出最终的解决方案。莫急,马上。
继续看上面代码的结尾处,mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);发送了个消息,接收者是ExitTransitionCoordinator,然后触发了hideSharedElements方法,最终调用了hideViews方法。
//ActivityTransitionCoordinator.class
protected void hideViews(ArrayList<View> views) {
int count = views.size();
for (int i = 0; i < count; i++) {
View view = views.get(i);
if (!mOriginalAlphas.containsKey(view)) {
mOriginalAlphas.put(view, view.getAlpha());
}
view.setAlpha(0f);
}
}
又将view的alpha设为了0,即全透明。走了一大圈,终于和onResume方法呼应了。所以解决方案就是,将view的alpha重新设为1。
本文只分析页面A进入页面B的过程,页面B返回页面A的过程,和解决问题没有直接关联,所以这里不做分析。有兴趣的,可以自行分析。
那在哪里设置呢?立马就想到,当然是B页面的onSharedElementEnd方法中通知页面A,将共享元素的view设为alpha = 1。
当然是错误的,因为就算设为了alpha = 1 ,最后又调用了hideViews方法,又设为了0。而且hideViews之后没有提供任何回调的方法。似乎又进入了死胡同。
回到项目中。既然没法知道真正的动画结束的时刻,那只能在其他时刻去将alpha设置为1。因为拖拽,我能知道拖拽开始的事件,所以在页面B拖拽开始的时候,通过Rxbus/EventBus通知页面A,将view的alpha设置为1。好了问题解决。
效果图
黑人问号脸。怎么在返回动画执行的时候,view又变空白了?
由于篇幅问题,长话短说了。因为返回动画是在页面A上面执行的,并且动画执行的时候,view的alpha又被设为0了。所以,只要在页面A的监听中,如下处理即可。
@Override
public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds) {
sharedElement.setAlpha(1f);
return super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds);
}
效果图
最后再推荐一波DragCloseHelper。具体的解决代码也在对应的demo中。
参考资料
https://www.jianshu.com/p/fa1c8deeaa57
备注:我的分析和文中略有出入,出入的地方在图中用红线标出。原因上面已经分析,当然也有可能是我分析错了,但是不影响大家了解整个过程。如果确定是我分析错了,请联系我,我会改正过来,感谢。