背景
前段時(shí)間寫了一篇Android 仿微信朋友圈圖片拖拽返回,有朋友指出為什么在拖拽的時(shí)候捏浊,發(fā)現(xiàn)上一個(gè)頁面點(diǎn)擊的圖片是空白的皮官,可以看下效果圖叭爱。
出現(xiàn)問題的本能反應(yīng)撮躁,先對(duì)比下微信朋友圈的效果,發(fā)現(xiàn)沒問題买雾。[手動(dòng)黑人問號(hào)臉]
后來無意中發(fā)現(xiàn)馒胆,當(dāng)手機(jī)休眠喚醒之后,這個(gè)問題就沒有了凝果。那就說明在onResume中的部分代碼對(duì)view做了處理。
onResume分析
既然發(fā)現(xiàn)onResume是沒問題的睦尽,所以進(jìn)入該方法器净。(注意,全篇都是基于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;
}
因?yàn)槭窃诠蚕碓匕l(fā)現(xiàn)的問題当凡,那就很明顯山害,主要就看mActivityTransitionState.onResume(this, isTopOfTask());這行代碼。繼續(xù)追蹤
//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);
}
}
其實(shí)就兩個(gè)關(guān)鍵方法沿量,restoreExitedViews();和restoreReenteringViews(); 那就分別看下這兩個(gè)方法做了什么浪慌。
restoreExitedViews,字面翻譯意思就是朴则,恢復(fù)已經(jīng)退出的view权纤。
//ActivityTransitionState.class
private void restoreExitedViews() {
if (mCalledExitCoordinator != null) {
mCalledExitCoordinator.resetViews();
mCalledExitCoordinator = null;
}
}
很簡(jiǎn)單,只有一行有效代碼乌妒,繼續(xù)跟進(jìn)汹想,進(jìn)入ExitTransitionCoordinator.class。
//ExitTransitionCoordinator.class
public void resetViews() {
ViewGroup decorView = getDecor();
if (decorView != null) {
//如果decorView不為空撤蚊,則強(qiáng)制將動(dòng)畫結(jié)束
TransitionManager.endTransitions(decorView);
}
if (mTransitioningViews != null) {
//如果有過渡動(dòng)畫的view的話古掏,就開始顯示view
showViews(mTransitioningViews, true);
setTransitioningViewsVisiblity(View.VISIBLE, true);
}
showViews(mSharedElements, true);
mIsHidden = true;
if (!mIsReturning && decorView != null) {
decorView.suppressLayout(false);
}
//從遮罩層上移除共享元素
moveSharedElementsFromOverlay();
//清除狀態(tài)
clearState();
}
關(guān)鍵方法1.showViews,2.setTransitioningViewsVisiblity侦啸,3.moveSharedElementsFromOverlay槽唾。其實(shí)看字面意思就是重新顯示view。不妨在跟蹤看下光涂。
進(jìn)入父類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);
}
}
本以為這個(gè)方法主要做的是setVisibility庞萍,結(jié)果發(fā)現(xiàn)主要做了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設(shè)為1,即不透明葵蒂,
setTransitioningViewsVisiblity 將view重新setVisiblity交播,
moveSharedElementsFromOverlay 從遮罩層中移除共享元素
其實(shí)到這里就已經(jīng)知道,為什么onResume之后就沒問題了践付。因?yàn)関iew setVisiblity 和 setAlpha了秦士。但是還是看下restoreReenteringViews方法。
//ActivityTransitionState.class
private void restoreReenteringViews() {
if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() &&
!mEnterTransitionCoordinator.isCrossTask()) {
//關(guān)鍵代碼
mEnterTransitionCoordinator.forceViewsToAppear();
mExitingFrom = null;
mExitingTo = null;
mExitingToView = null;
}
}
繼續(xù)進(jìn)入關(guān)鍵代碼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();
}
****省略部分代碼****
}
其實(shí)會(huì)發(fā)現(xiàn)永高,和上面分析的restoreExitedViews方法差不多隧土,基本都已經(jīng)分析過了。所以命爬,這個(gè)問題到目前為止曹傀,其實(shí)已經(jīng)算結(jié)束了。只要在共享元素動(dòng)畫結(jié)束之后饲宛,再手動(dòng)的調(diào)一次onResume方法即可皆愉。但是,onResume方法太重了艇抠,還沒見過解決方法用的這么重的方法的幕庐。那就繼續(xù)分析吧。
共享元素分析
略長家淤,對(duì)源碼分析不感興趣的异剥,可以直接滑到最后。
首先絮重,共享元素的動(dòng)畫監(jiān)聽冤寿,可以通過setExitSharedElementCallback 和 setEnterSharedElementCallback監(jiān)聽。其中共有7個(gè)方法青伤,如下
//動(dòng)畫開始
void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)
//動(dòng)畫結(jié)束
void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)
//被移除的共享元素疚沐,即不需要進(jìn)行共享元素動(dòng)畫的view
void onRejectSharedElements(List<View> rejectedSharedElements)
//共享元素view和name的鍵值對(duì)
void onMapSharedElements(List<String> names, Map<String, View> sharedElements)
//將view的信息以parcelable的對(duì)象類型保存,用于activity之間傳遞
Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds)
//將parcelable對(duì)象重新轉(zhuǎn)成view
View onCreateSnapshotView(Context context, Parcelable snapshot)
//共享元素已經(jīng)拿到
void onSharedElementsArrived(List<String> sharedElementNames, List<View> sharedElements, OnSharedElementsReadyListener listener)
我們不妨先打印下log潮模,看下調(diào)用順序亮蛔。
注意:A進(jìn)入B的過程,A使用setExitSharedElementCallback退出監(jiān)聽擎厢,B使用setEnterSharedElementCallback進(jìn)入監(jiān)聽究流。
結(jié)果如下
A進(jìn)入B
A:
onMapSharedElements
onCaptureSharedElementSnapshot
onSharedElementsArrived
B:
onMapSharedElements
onSharedElementsArrived
onRejectSharedElements
onCreateSnapshotView
onSharedElementStart
onSharedElementEnd
B返回到A
B:
onMapSharedElements
A:
onMapSharedElements
onCaptureSharedElementSnapshot
B:
onCreateSnapshotView
onSharedElementEnd//沒看錯(cuò),先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開始动遭,一步步跳轉(zhuǎn)芬探,進(jìn)入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以鍵值對(duì)的形式保存
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);
}
}
//創(chuàng)建ExitTransitionCoordinator
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
callback, names, names, views, false);
****省略部分代碼****
return exit;
}
主要就是將view和name以鍵值對(duì)的形式保存,并且創(chuàng)建了ExitTransitionCoordinator對(duì)象厘惦,進(jìn)入此對(duì)象
//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;
}
在進(jìn)入父類的viewsReady
//ActivityTransitionCoordinator.class
protected void viewsReady(ArrayMap<String, View> sharedElements) {
sharedElements.retainAll(mAllSharedElementNames);
if (mListener != null) {
mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
}
setSharedElements(sharedElements);
****省略部分代碼****
}
成功找到第一個(gè)回調(diào)onMapSharedElements偷仿。所以ActivityOptionsCompat.makeSceneTransitionAnimation哩簿,主要就是將view和對(duì)應(yīng)的name保存到ExitTransitionCoordinator對(duì)象中,期間會(huì)執(zhí)行onMapSharedElements回調(diào)酝静。
然后分析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();
}
}
});
}
}
主要就是兩個(gè)方法节榜,1.moveSharedElementsToOverlay,2.startTransition别智。方法1理解字面意思即可宗苍。主要還是方法2。
因?yàn)閙Activity不為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的監(jiān)聽
//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;
}
這里會(huì)進(jìn)入重寫的sharedElementTransitionComplete讳窟,
//ExitTransitionCoordinator.class
@Override
protected void sharedElementTransitionComplete() {
mSharedElementBundle = mExitSharedElementBundle == null
? captureSharedElementState() : captureExitSharedElementsState();
super.sharedElementTransitionComplete();
}
此處會(huì)進(jìn)入captureSharedElementState 方法以及super.sharedElementTransitionComplete();方法。
先進(jìn)入父類的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)回調(diào)丽啡。
同時(shí)super.sharedElementTransitionComplete();會(huì)進(jìn)入startInputWhenTransitionsComplete,再進(jìn)入onTransitionsComplete硬猫,最后進(jìn)入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();
}
}
}
但是因?yàn)榇颂巌sReadyToNotify()為false补箍,所以這個(gè)方法等于沒有執(zhí)行。所以到目前為止浦徊,A頁面已經(jīng)沒法繼續(xù)分析了。
開始分析頁面B天梧。頁面B會(huì)經(jīng)過onCreate盔性,onStart等方法。找到performStart方法呢岗,再找到mActivityTransitionState.enterReady(this); 進(jìn)入enterReady
//ActivityTransitionState.class
public void enterReady(Activity activity) {
****省略部分代碼****
ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
if (mEnterActivityOptions.isReturning()) {
//這個(gè)方法很熟悉冕香,在onResume中分析過
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
//創(chuàng)建了EnterTransitionCoordinator對(duì)象
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();
}
}
發(fā)現(xiàn)創(chuàng)建了EnterTransitionCoordinator對(duì)象,最后調(diào)用了startEnter方法后豫。
先分析EnterTransitionCoordinator對(duì)象悉尾。進(jìn)入構(gòu)造方法
//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);方法,發(fā)送的消息挫酿,接收者就是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;
此處會(huì)執(zhí)行notifyComplete,同時(shí)notifyComplete方法中的isReadyToNotify()為true早龟,所以此處就會(huì)進(jìn)入onSharedElementsArrived回調(diào)惫霸。到目前為止,A頁面的回調(diào)已經(jīng)全部找到葱弟。
//ExitTransitionCoordinator.class
protected boolean isReadyToNotify() {
return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}
isReadyToNotify方法中mResultReceiver這個(gè)對(duì)象壹店,只有在頁面B發(fā)送MSG_SET_REMOTE_RECEIVER這個(gè)消息之后,才會(huì)賦值芝加。
最后notifyComplete方法中會(huì)有這么一行代碼resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, sharedElementBundle); 內(nèi)部通過handler發(fā)送了一個(gè)消息硅卢,發(fā)送給誰?我猜應(yīng)該是對(duì)應(yīng)的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;
****省略部分代碼****
}
}
對(duì)mSharedElementsBundle賦值脉顿,然后調(diào)用onTakeSharedElements方法。到這里先暫停下抬旺。
接著上面的startEnter方法弊予,然后調(diào)用namedViewsReady方法,再調(diào)用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();
}
}
然后進(jìn)入復(fù)寫的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();
}
}
此處有兩個(gè)注意點(diǎn)汉柒,1.super.viewsReady(sharedElements); 2.最后的onTakeSharedElements();
父類的viewsReady,上面已經(jīng)有分析责鳍,會(huì)回調(diào)onMapSharedElements方法碾褂,因?yàn)槭荅nterTransitionCoordinator類,所以是頁面B的回調(diào)历葛。
重點(diǎn)是onTakeSharedElements方法正塌。首先,剛才暫停的一段代碼恤溶,也是這個(gè)方法乓诽。在當(dāng)前類EnterTransitionCoordinator中搜索此方法,總共就這兩處調(diào)用咒程。但是這里調(diào)用前提是mSharedElementsBundle != null鸠天,而mSharedElementsBundle只有在前面的地方賦值,重復(fù)粘貼一遍代碼帐姻,如下
//EnterTransitionCoordinator.class
case MSG_TAKE_SHARED_ELEMENTS:
if (!mIsCanceled) {
mSharedElementsBundle = resultData;
onTakeSharedElements();
}
break;
所以稠集,真正執(zhí)行onTakeSharedElements方法的,是在收到MSG_TAKE_SHARED_ELEMENTS消息之后饥瓷。繼續(xù)分析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);
}
}
這里調(diào)用了B頁面的onSharedElementsArrived剥纷。同時(shí)監(jiān)聽的回調(diào)中調(diào)用了startSharedElementTransition方法。
//EnterTransitionCoordinator.class
private void startSharedElementTransition(Bundle sharedElementState) {
****省略部分代碼****
ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
rejectedNames.removeAll(mSharedElementNames);
//創(chuàng)建非共享元素的view
ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
if (mListener != null) {
//回調(diào)onRejectSharedElements
mListener.onRejectSharedElements(rejectedSnapshots);
}
removeNullViews(rejectedSnapshots);
//執(zhí)行非共享元素的動(dòng)畫
startRejectedAnimations(rejectedSnapshots);
// 創(chuàng)建共享元素的view呢铆,此處會(huì)回調(diào)頁面B的onCreateSnapshotView方法
ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
mSharedElementNames);
showViews(mSharedElements, true);
//此處會(huì)回調(diào)頁面B的onSharedElementEnd方法
scheduleSetSharedElementEnd(sharedElementSnapshots);
//此處會(huì)回調(diào)頁面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.
}
}
});
}
}
此處就會(huì)調(diào)用頁面B的其他方法晦鞋。已經(jīng)在上方代碼中備注。目前為止頁面A進(jìn)入頁面B棺克,已經(jīng)大概的過了一遍鳖宾。
坑啊,都N久了逆航,還沒給出最終的解決方案鼎文。莫急,馬上因俐。
繼續(xù)看上面代碼的結(jié)尾處拇惋,mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);發(fā)送了個(gè)消息周偎,接收者是ExitTransitionCoordinator,然后觸發(fā)了hideSharedElements方法撑帖,最終調(diào)用了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設(shè)為了0,即全透明胡嘿。走了一大圈蛉艾,終于和onResume方法呼應(yīng)了。所以解決方案就是衷敌,將view的alpha重新設(shè)為1勿侯。
本文只分析頁面A進(jìn)入頁面B的過程,頁面B返回頁面A的過程缴罗,和解決問題沒有直接關(guān)聯(lián)助琐,所以這里不做分析。有興趣的面氓,可以自行分析兵钮。
那在哪里設(shè)置呢?立馬就想到舌界,當(dāng)然是B頁面的onSharedElementEnd方法中通知頁面A掘譬,將共享元素的view設(shè)為alpha = 1。
當(dāng)然是錯(cuò)誤的呻拌,因?yàn)榫退阍O(shè)為了alpha = 1 葱轩,最后又調(diào)用了hideViews方法,又設(shè)為了0柏锄。而且hideViews之后沒有提供任何回調(diào)的方法酿箭。似乎又進(jìn)入了死胡同复亏。
回到項(xiàng)目中趾娃。既然沒法知道真正的動(dòng)畫結(jié)束的時(shí)刻,那只能在其他時(shí)刻去將alpha設(shè)置為1缔御。因?yàn)橥献疲夷苤劳献ч_始的事件,所以在頁面B拖拽開始的時(shí)候耕突,通過Rxbus/EventBus通知頁面A笤成,將view的alpha設(shè)置為1。好了問題解決眷茁。
效果圖
黑人問號(hào)臉炕泳。怎么在返回動(dòng)畫執(zhí)行的時(shí)候,view又變空白了上祈?
由于篇幅問題培遵,長話短說了浙芙。因?yàn)榉祷貏?dòng)畫是在頁面A上面執(zhí)行的,并且動(dòng)畫執(zhí)行的時(shí)候籽腕,view的alpha又被設(shè)為0了嗡呼。所以,只要在頁面A的監(jiān)聽中皇耗,如下處理即可南窗。
@Override
public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds) {
sharedElement.setAlpha(1f);
return super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds);
}
效果圖
最后再推薦一波DragCloseHelper。具體的解決代碼也在對(duì)應(yīng)的demo中郎楼。
參考資料
http://www.reibang.com/p/fa1c8deeaa57
備注:我的分析和文中略有出入万伤,出入的地方在圖中用紅線標(biāo)出。原因上面已經(jīng)分析箭启,當(dāng)然也有可能是我分析錯(cuò)了壕翩,但是不影響大家了解整個(gè)過程。如果確定是我分析錯(cuò)了傅寡,請(qǐng)聯(lián)系我放妈,我會(huì)改正過來,感謝荐操。