欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?Activity共享元素動(dòng)畫示例解析

 更新時(shí)間:2022年09月19日 16:03:12   作者:一針見(jiàn)血  
這篇文章主要為大家介紹了Android?Activity共享元素動(dòng)畫示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

所謂Activity共享元素動(dòng)畫,就是從ActivityA跳轉(zhuǎn)到ActivityB 通過(guò)控制某些元素(View)從ActivityA開(kāi)始幀的位置跳轉(zhuǎn)到ActivityB 結(jié)束幀的位置,應(yīng)用過(guò)度動(dòng)畫

Activity的共享元素動(dòng)畫,其動(dòng)畫核心是使用的Transition記錄共享元素的開(kāi)始幀、結(jié)束幀,然后使用TransitionManager過(guò)度動(dòng)畫管理類調(diào)用beginDelayedTransition方法 應(yīng)用過(guò)度動(dòng)畫

注意:Android5.0才開(kāi)始支持共享元素動(dòng)畫

所以咱們先介紹一下TransitionManager的一些基礎(chǔ)知識(shí)

TransitionManager介紹

TransitionManagerAndroid5.0開(kāi)始提供的一個(gè)過(guò)渡動(dòng)畫管理類,功能非常強(qiáng)大;其可應(yīng)用在兩個(gè)Activity之間、Fragment之間、View之間應(yīng)用過(guò)渡動(dòng)畫

TransitionManager有兩個(gè)比較重要的類Scene(場(chǎng)景)Transition(過(guò)渡) , 咱們先來(lái)介紹一下這兩個(gè)類

Scene(場(chǎng)景)

顧名思義Scene就是場(chǎng)景的意思,在執(zhí)行動(dòng)畫之前,我們需要?jiǎng)?chuàng)建兩個(gè)場(chǎng)景(場(chǎng)景A和場(chǎng)景B), 其動(dòng)畫執(zhí)行流程如下:

  • 根據(jù)起始布局和結(jié)束布局創(chuàng)建兩個(gè) Scene 對(duì)象(場(chǎng)景A和場(chǎng)景B); 然而 起始布局的場(chǎng)景通常是根據(jù)當(dāng)前布局自動(dòng)確定的
  • 創(chuàng)建一個(gè) Transition 對(duì)象以定義所需的動(dòng)畫類型
  • 調(diào)用 TransitionManager.go(Scene, Transition),使用過(guò)渡動(dòng)畫運(yùn)行到指定的場(chǎng)景

生成場(chǎng)景

生成場(chǎng)景有兩種方式; 一種是調(diào)用靜態(tài)方法通過(guò)布局生成 Scene.getSceneForLayout(sceneRoot, R.layout.scene_a, this),一種是直接通過(guò)構(gòu)造方法new Scene(sceneRoot, viewHierarchy)指定view對(duì)象生成

這兩種方式其實(shí)差不多,第一種通過(guò)布局生成的方式在使用的時(shí)候會(huì)自動(dòng)inflate加載布局生成view對(duì)象

用法比較簡(jiǎn)單;下面我們來(lái)看一下官方的demo

定義兩個(gè)布局場(chǎng)景A和場(chǎng)景B

<!-- res/layout/scene_a.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/text_view2"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 2(a)" />
    <TextView
        android:id="@+id/text_view1"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 1(a)" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 3(a)" />
</LinearLayout>
<!-- res/layout/scene_b.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/text_view1"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 1(b)" />
    <TextView
        android:id="@+id/text_view2"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 2(b)" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Text Line 3(b)" />
</LinearLayout>

使用場(chǎng)景并執(zhí)行動(dòng)畫

// 創(chuàng)建a場(chǎng)景
val aScene: Scene = Scene.getSceneForLayout(binding.sceneRoot, R.layout.scene_a, this)
// 創(chuàng)建b場(chǎng)景
val bScene: Scene = Scene.getSceneForLayout(binding.sceneRoot, R.layout.scene_b, this)
var aSceneFlag = true
// 添加點(diǎn)擊事件,切換不同的場(chǎng)景
binding.btClick1.setOnClickListener {
    if (aSceneFlag) {
        TransitionManager.go(bScene)
        aSceneFlag = false
    } else {
        TransitionManager.go(aScene)
        aSceneFlag = true
    }
}

執(zhí)行效果如下:

通過(guò)上面的效果可以看出,切換的一瞬間會(huì)立馬變成指定場(chǎng)景的所有view(文案全都變了),只是應(yīng)用了開(kāi)始幀的位置而已,然后慢慢過(guò)渡到結(jié)束幀的位置;

// Scene的enter()方法源碼
public void enter() {
    // Apply layout change, if any
    if (mLayoutId > 0 || mLayout != null) {
        // remove掉場(chǎng)景根視圖下的所有view(即上一個(gè)場(chǎng)景)
        getSceneRoot().removeAllViews();
		// 添加當(dāng)前場(chǎng)景的所有view
        if (mLayoutId > 0) {
            LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
        } else {
            mSceneRoot.addView(mLayout);
        }
    }
    // Notify next scene that it is entering. Subclasses may override to configure scene.
    if (mEnterAction != null) {
        mEnterAction.run();
    }
    setCurrentScene(mSceneRoot, this);
}

可見(jiàn)切換到指定場(chǎng)景,比如切換到場(chǎng)景b, 會(huì)remove掉場(chǎng)景a的所有view,然后添加場(chǎng)景b的所有view

其次;這種方式的兩種場(chǎng)景之間的切換動(dòng)畫;是通過(guò)id確定兩個(gè)view之間的對(duì)應(yīng)關(guān)系,從而確定view的開(kāi)始幀和結(jié)束幀 來(lái)執(zhí)行過(guò)渡動(dòng)畫;如果沒(méi)有id對(duì)應(yīng)關(guān)系的view(即沒(méi)有開(kāi)始幀或結(jié)束幀), 會(huì)執(zhí)行刪除動(dòng)畫(默認(rèn)是漸隱動(dòng)畫)或添加動(dòng)畫(默認(rèn)是漸顯動(dòng)畫)(看源碼也可以通過(guò)transtionName屬性來(lái)指定對(duì)應(yīng)關(guān)系)

其視圖匹配對(duì)應(yīng)關(guān)系的源碼如下:

// startValues 是開(kāi)始幀所有對(duì)象的屬性
// endValues 是結(jié)束幀所有對(duì)象的屬性
private void matchStartAndEnd(TransitionValuesMaps startValues,
        TransitionValuesMaps endValues) {
    ArrayMap<View, TransitionValues> unmatchedStart =
            new ArrayMap<View, TransitionValues>(startValues.viewValues);
    ArrayMap<View, TransitionValues> unmatchedEnd =
            new ArrayMap<View, TransitionValues>(endValues.viewValues);
    for (int i = 0; i < mMatchOrder.length; i++) {
        switch (mMatchOrder[i]) {
            case MATCH_INSTANCE:
            	// 匹配是否相同的對(duì)象(可以通過(guò)改變view的屬性,使用過(guò)渡動(dòng)畫)
                matchInstances(unmatchedStart, unmatchedEnd);
                break;
            case MATCH_NAME:
            	// 匹配transitionName屬性是否相同(activity之間就是通過(guò)transtionName來(lái)匹配的)
                matchNames(unmatchedStart, unmatchedEnd,
                        startValues.nameValues, endValues.nameValues);
                break;
            case MATCH_ID:
            	// 匹配view的id是否相同
                matchIds(unmatchedStart, unmatchedEnd,
                        startValues.idValues, endValues.idValues);
                break;
            case MATCH_ITEM_ID:
            	// 特殊處理listview的item
                matchItemIds(unmatchedStart, unmatchedEnd,
                        startValues.itemIdValues, endValues.itemIdValues);
                break;
        }
    }
    // 添加沒(méi)有匹配到的對(duì)象
    addUnmatched(unmatchedStart, unmatchedEnd);
}

可見(jiàn)試圖的匹配關(guān)系有很多種;可以根據(jù) 視圖對(duì)象本身、視圖的id、視圖的transitionName屬性等匹配對(duì)應(yīng)關(guān)系

定義場(chǎng)景比較簡(jiǎn)單,其實(shí)相對(duì)比較復(fù)雜的是Transition過(guò)度動(dòng)畫

缺點(diǎn):個(gè)人覺(jué)得通過(guò)創(chuàng)建不同Scene對(duì)象實(shí)現(xiàn)動(dòng)畫效果比較麻煩,需要?jiǎng)?chuàng)建多套布局,后期難以維護(hù);所以一般這種使用TransitionManager.go(bScene)方法指定Scene對(duì)象的方式基本不常用,一般都是使用TransitionManager.beginDelayedTransition()方法來(lái)實(shí)現(xiàn)過(guò)渡動(dòng)畫

下面我們來(lái)介紹Transition,并配合使用TransitionManager.beginDelayedTransition()方法實(shí)現(xiàn)動(dòng)畫效果

Transition(過(guò)渡)

顧名思義 Transition 是過(guò)渡的意思,里面定義了怎么 記錄開(kāi)始幀的屬性、記錄結(jié)束幀的屬性、創(chuàng)建動(dòng)畫或執(zhí)行動(dòng)畫的邏輯

我們先看看Transition源碼里比較重要的幾個(gè)方法

// android.transition.Transition的源碼
public abstract class Transition implements Cloneable {
	...
	// 通過(guò)實(shí)現(xiàn)這個(gè)方法記錄view的開(kāi)始幀的屬性
	public abstract void captureStartValues(TransitionValues transitionValues);
	// 通過(guò)實(shí)現(xiàn)這個(gè)方法記錄view的結(jié)束幀的屬性
	public abstract void captureEndValues(TransitionValues transitionValues);
	// 通過(guò)記錄的開(kāi)始幀和結(jié)束幀的屬性,創(chuàng)建動(dòng)畫
	// 默認(rèn)返回null,即沒(méi)有動(dòng)畫;需要你自己創(chuàng)建動(dòng)畫對(duì)象
	public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        return null;
    }
    // 執(zhí)行動(dòng)畫
    // mAnimators 里包含的就是上面createAnimator()方法創(chuàng)建的動(dòng)畫對(duì)象
    protected void runAnimators() {
        if (DBG) {
            Log.d(LOG_TAG, "runAnimators() on " + this);
        }
        start();
        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
        // Now start every Animator that was previously created for this transition
        for (Animator anim : mAnimators) {
            if (DBG) {
                Log.d(LOG_TAG, "  anim: " + anim);
            }
            if (runningAnimators.containsKey(anim)) {
                start();
                runAnimator(anim, runningAnimators);
            }
        }
        mAnimators.clear();
        end();
    }
    ...
}

如果我們要自定義Transition 過(guò)渡動(dòng)畫的話,一般只需要重寫前三個(gè)方法即可

當(dāng)前系統(tǒng)也提供了一套完成的Transition過(guò)渡動(dòng)畫的子類

上面這些都是系統(tǒng)提供的Transition子類 的 實(shí)現(xiàn)效果 和 其在captureStartValues、captureEndValues中記錄的屬性,然后在createAnimator方法中創(chuàng)建的屬性動(dòng)畫 不斷改變的屬性

當(dāng)然除了上面的一些類以外,系統(tǒng)還提供了TransitionSet類,可以指定一組動(dòng)畫;它也是的Transition子類

TransitionManager中的默認(rèn)動(dòng)畫就是 AutoTransition , 它是TransitionSet的子類

public class AutoTransition extends TransitionSet {
    public AutoTransition() {
        init();
    }
    public AutoTransition(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init() {
        setOrdering(ORDERING_SEQUENTIAL);
        addTransition(new Fade(Fade.OUT)).
                addTransition(new ChangeBounds()).
                addTransition(new Fade(Fade.IN));
    }
}

可見(jiàn)AutoTransition包含了 淡出、位移、改變大小、淡入 等一組效果

下面我們來(lái)自定義一些Transition,達(dá)到一些效果

// 記錄和改變translationX、translationY屬性
class XYTranslation : Transition() {
    override fun captureStartValues(transitionValues: TransitionValues?) {
        transitionValues ?: return
        transitionValues.values["translationX"] = transitionValues.view.translationX
        transitionValues.values["translationY"] = transitionValues.view.translationY
    }
    override fun captureEndValues(transitionValues: TransitionValues?) {
        transitionValues ?: return
        transitionValues.values["translationX"] = transitionValues.view.translationX
        transitionValues.values["translationY"] = transitionValues.view.translationY
    }
    override fun createAnimator(
        sceneRoot: ViewGroup?,
        startValues: TransitionValues?,
        endValues: TransitionValues?
    ): Animator? {
        if (startValues == null || endValues == null) return null
        val startX = startValues.values["translationX"] as Float
        val startY = startValues.values["translationY"] as Float
        val endX = endValues.values["translationX"] as Float
        val endY = endValues.values["translationY"] as Float
        var translationXAnim: Animator? = null
        if (startX != endX) {
            translationXAnim = ObjectAnimator.ofFloat(endValues.view, "translationX", startX, endX)
        }
        var translationYAnim: Animator? = null
        if (startY != endY) {
            translationYAnim = ObjectAnimator.ofFloat(endValues.view, "translationY", startY, endY)
        }
        return mergeAnimators(translationXAnim, translationYAnim)
    }
    fun mergeAnimators(animator1: Animator?, animator2: Animator?): Animator? {
        return if (animator1 == null) {
            animator2
        } else if (animator2 == null) {
            animator1
        } else {
            val animatorSet = AnimatorSet()
            animatorSet.playTogether(animator1, animator2)
            animatorSet
        }
    }
}
// 記錄和改變backgroundColor屬性
class BackgroundColorTransition : Transition() {
    override fun captureStartValues(transitionValues: TransitionValues?) {
        transitionValues ?: return
        val drawable = transitionValues.view.background as? ColorDrawable ?: return
        transitionValues.values["backgroundColor"] = drawable.color
    }
    override fun captureEndValues(transitionValues: TransitionValues?) {
        transitionValues ?: return
        val drawable = transitionValues.view.background as? ColorDrawable ?: return
        transitionValues.values["backgroundColor"] = drawable.color
    }
    override fun createAnimator(
        sceneRoot: ViewGroup?,
        startValues: TransitionValues?,
        endValues: TransitionValues?
    ): Animator? {
        if (startValues == null || endValues == null) return null
        val startColor = (startValues.values["backgroundColor"] as? Int) ?: return null
        val endColor = (endValues.values["backgroundColor"] as? Int) ?: return null
        if (startColor != endColor) {
            return ObjectAnimator.ofArgb(endValues.view, "backgroundColor", startColor, endColor)
        }
        return super.createAnimator(sceneRoot, startValues, endValues)
    }
}

非常簡(jiǎn)單,上面就自定義了兩個(gè)XYTranslation BackgroundColorTransition 類,實(shí)現(xiàn)位移和改變背景顏色的效果

下面我們配合使用TransitionManager.beginDelayedTransition()方法,應(yīng)用XYTranslation BackgroundColorTransition 兩個(gè)動(dòng)畫過(guò)渡類,實(shí)現(xiàn)效果

<!-- res/layout/activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/btClick"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="執(zhí)行動(dòng)畫"/>
    <LinearLayout
        android:id="@+id/beginDelayRoot"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="#ffff00"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="Text Line 1" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="Text Line 2" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="Text Line 3" />
    </LinearLayout>
</LinearLayout>
class TransitionDemoActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
        setContentView(binding.root)
        val backgroundColor1 = Color.parseColor("#ffff00")
        val backgroundColor2 = Color.parseColor("#00ff00")
        var index = 0
        binding.btClick.setOnClickListener {
            val view1 = binding.beginDelayRoot.getChildAt(0)
            val view2 = binding.beginDelayRoot.getChildAt(1)
            // 設(shè)置開(kāi)始位置的x偏移量為100px(定義開(kāi)始幀的屬性)
            view1.translationX = 100f
            view2.translationX = 100f
            // 調(diào)用beginDelayedTransition 會(huì)立馬調(diào)用 Transition的captureStartValues方法記錄開(kāi)始幀
            // 同時(shí)會(huì)添加一個(gè)OnPreDrawListener, 在屏幕刷新的下一幀觸發(fā)onPreDraw() 方法,然后調(diào)用captureEndValues方法記錄結(jié)束幀,然后開(kāi)始執(zhí)行動(dòng)畫
            TransitionManager.beginDelayedTransition(binding.beginDelayRoot, TransitionSet().apply {
            	// 實(shí)現(xiàn)上下移動(dòng)(因?yàn)闆](méi)有改變view的left屬性所以, 所以它沒(méi)有左右移動(dòng)效果)
                addTransition(ChangeBounds())
                // 通過(guò)translationX屬性實(shí)現(xiàn)左右移動(dòng)
                addTransition(XYTranslation()) 
                // 通過(guò)backgroundColor屬性改變背景顏色
                addTransition(BackgroundColorTransition())
            })
            // 下面開(kāi)始改變視圖的屬性(定義結(jié)束幀的屬性)
            // 將結(jié)束位置x偏移量為0px
            view1.translationX = 0f
            view2.translationX = 0f
            binding.beginDelayRoot.removeView(view1)
            binding.beginDelayRoot.removeView(view2)
            binding.beginDelayRoot.addView(view1)
            binding.beginDelayRoot.addView(view2)
            binding.beginDelayRoot.setBackgroundColor(if (index % 2 == 0) backgroundColor2 else backgroundColor1)
            index++
        }
    }
}

其效果圖如下:

你可能會(huì)有些疑問(wèn),為什么上面將translationX設(shè)置成100之后,立馬又改成了0;這樣有什么意義嗎??

可見(jiàn)Transition的使用和自定義也比較簡(jiǎn)單,同時(shí)也能達(dá)到一些比較炫酷的效果

請(qǐng)注意,改變view的屬性并不會(huì)立馬重新繪制視圖,而是在屏幕的下一幀(60fps的話,就是16毫秒一幀)去繪制;而在繪制下一幀之前調(diào)用了TransitionManager.beginDelayedTransition()方法,里面會(huì)觸發(fā)XYTransitioncaptureStartValues方法記錄開(kāi)始幀(記錄的translationX為100),同時(shí)TransitionManager會(huì)添加OnPreDrawListener, 在屏幕下一幀到來(lái)觸發(fā)view去繪制的時(shí)候,會(huì)先調(diào)用OnPreDrawListeneronPreDraw() 方法,里面又會(huì)觸發(fā)XYTransitioncaptureEndValues方法記錄結(jié)束幀的屬性(記錄的translationX為0), 然后應(yīng)用動(dòng)畫 改變view的屬性,最后交給view去繪制

上面講了這么多,下面我們簡(jiǎn)單分析一下TransitionManager.beginDelayedTransition方法的源碼

首先是TransitionManagerbeginDelayedTransition方法

// android.transition.TransitionManager源碼
public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
    if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
        if (Transition.DBG) {
            Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
                    sceneRoot + ", " + transition);
        }
        sPendingTransitions.add(sceneRoot);
        if (transition == null) {
            transition = sDefaultTransition;
        }
        final Transition transitionClone = transition.clone();
        sceneChangeSetup(sceneRoot, transitionClone);
        Scene.setCurrentScene(sceneRoot, null);
        sceneChangeRunTransition(sceneRoot, transitionClone);
    }
}

里面代碼比較少;我們主要看sceneChangeSetupsceneChangeRunTransition方法的實(shí)現(xiàn)

// android.transition.TransitionManager源碼
private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
    // Capture current values
    ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
    if (runningTransitions != null && runningTransitions.size() > 0) {
        for (Transition runningTransition : runningTransitions) {
        	// 暫停正在運(yùn)行的動(dòng)畫
            runningTransition.pause(sceneRoot);
        }
    }
    if (transition != null) {
    	// 調(diào)用transition.captureValues;
    	// 其實(shí)第二個(gè)參數(shù) true或false,表示是開(kāi)始還是結(jié)束,對(duì)應(yīng)會(huì)調(diào)用captureStartValues和captureEndValues 方法
        transition.captureValues(sceneRoot, true);
    }
	...
}

我們來(lái)簡(jiǎn)單看看transition.captureValues的源碼

// android.transition.Transition源碼
void captureValues(ViewGroup sceneRoot, boolean start) {
    clearValues(start);
    // 如果你的 Transition 指定了目標(biāo)view,就會(huì)執(zhí)行這個(gè)if
    if ((mTargetIds.size() > 0 || mTargets.size() > 0)
            && (mTargetNames == null || mTargetNames.isEmpty())
            && (mTargetTypes == null || mTargetTypes.isEmpty())) {
        for (int i = 0; i < mTargetIds.size(); ++i) {
            int id = mTargetIds.get(i);
            View view = sceneRoot.findViewById(id);
            if (view != null) {
                TransitionValues values = new TransitionValues(view);
                if (start) {
                	// 記錄開(kāi)始幀的屬性
                    captureStartValues(values);
                } else {
                	// 記錄結(jié)束幀的屬性
                    captureEndValues(values);
                }
                values.targetedTransitions.add(this);
                capturePropagationValues(values);
                if (start) {
                	// 緩存開(kāi)始幀的屬性到mStartValues中
                    addViewValues(mStartValues, view, values);
                } else {
                	// 緩存結(jié)束幀的屬性到mEndValues中
                    addViewValues(mEndValues, view, values);
                }
            }
        }
        for (int i = 0; i < mTargets.size(); ++i) {
            View view = mTargets.get(i);
            TransitionValues values = new TransitionValues(view);
            if (start) {
            	// 記錄開(kāi)始幀的屬性
                captureStartValues(values);
            } else {
            	// 記錄結(jié)束幀的屬性
                captureEndValues(values);
            }
            values.targetedTransitions.add(this);
            capturePropagationValues(values);
            if (start) {
            	// 緩存開(kāi)始幀的屬性到mStartValues中
                addViewValues(mStartValues, view, values);
            } else {
            	// 緩存結(jié)束幀的屬性到mEndValues中
                addViewValues(mEndValues, view, values);
            }
        }
    } else {
    	// 沒(méi)有指定目標(biāo)view的情況
        captureHierarchy(sceneRoot, start);
    }
    ...
}
private void captureHierarchy(View view, boolean start) {
	...
    if (view.getParent() instanceof ViewGroup) {
        TransitionValues values = new TransitionValues(view);
        if (start) {
        	// 記錄開(kāi)始幀的屬性
            captureStartValues(values);
        } else {
        	// 記錄結(jié)束幀的屬性
            captureEndValues(values);
        }
        values.targetedTransitions.add(this);
        capturePropagationValues(values);
        if (start) {
        	// 緩存開(kāi)始幀的屬性到mStartValues中
            addViewValues(mStartValues, view, values);
        } else {
        	// 緩存結(jié)束幀的屬性到mEndValues中
            addViewValues(mEndValues, view, values);
        }
    }
    if (view instanceof ViewGroup) {
        // 遞歸遍歷所有的children
        ViewGroup parent = (ViewGroup) view;
        for (int i = 0; i < parent.getChildCount(); ++i) {
            captureHierarchy(parent.getChildAt(i), start);
        }
    }
}

可見(jiàn)sceneChangeSetup方法就會(huì)觸發(fā)TransitioncaptureStartValues 方法

接下來(lái)我們來(lái)看看sceneChangeRunTransition方法

// android.transition.TransitionManager源碼
private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
            final Transition transition) {
    if (transition != null && sceneRoot != null) {
        MultiListener listener = new MultiListener(transition, sceneRoot);
        sceneRoot.addOnAttachStateChangeListener(listener);
        // 添加OnPreDrawListener
        sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
    }
}
private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
            View.OnAttachStateChangeListener {
	...
    @Override
    public boolean onPreDraw() {
        removeListeners();
        ...
        // 這里就會(huì)觸發(fā)captureEndValues方法,記錄結(jié)束幀的屬性
        mTransition.captureValues(mSceneRoot, false);
        if (previousRunningTransitions != null) {
            for (Transition runningTransition : previousRunningTransitions) {
                runningTransition.resume(mSceneRoot);
            }
        }
        // 開(kāi)始執(zhí)行動(dòng)畫
        // 這里就會(huì)調(diào)用 Transition的createAnimator方法 和 runAnimators方法
        mTransition.playTransition(mSceneRoot);
        return true;
    }
};

TransitionplayTransition沒(méi)啥好看的,至此TransitionManagerbeginDelayedTransition源碼分析到這里

上面源碼里你可能也看到了Transition可以設(shè)置目標(biāo)視圖,應(yīng)用過(guò)渡動(dòng)畫, 主要是通過(guò)addTarget方法實(shí)現(xiàn)的,如果沒(méi)有設(shè)置目標(biāo)視圖,默認(rèn)就會(huì)遍歷所有的children應(yīng)用在所有的視圖上

OverlayView和ViewGroupOverlay

OverlayViewViewGroupOverlayActivity共享元素動(dòng)畫實(shí)現(xiàn)里比較重要的一個(gè)類,所以就單獨(dú)的介紹一下

OverlayView是針對(duì)View的一個(gè)頂層附加層(即遮罩層),它在View的所有內(nèi)容繪制完成之后 再繪制

ViewGroupOverlay是針對(duì)ViewGroup的,是OverlayView的子類,它在ViewGroup的所有內(nèi)容(包括所有的children)繪制完成之后 再繪制

// android.view.View源碼
public ViewOverlay getOverlay() {
    if (mOverlay == null) {
        mOverlay = new ViewOverlay(mContext, this);
    }
    return mOverlay;
}
// android.view.ViewGroup源碼
@Override
public ViewGroupOverlay getOverlay() {
    if (mOverlay == null) {
        mOverlay = new ViewGroupOverlay(mContext, this);
    }
    return (ViewGroupOverlay) mOverlay;
}

看上面的源碼我們知道,可以直接調(diào)用getOverlay方法直接獲取OverlayViewViewGroupOverlay對(duì)象, 然后我們就可以在上面添加一些裝飾等效果

OverlayView只支持添加drawable

ViewGroupOverlay支持添加Viewdrawable

注意:如果View 的parent不為null, 則會(huì)自動(dòng)先把它從parent中remove掉,然后添加到ViewGroupOverlay

核心源碼如下:

// OverlayViewGroup的add方法源碼
public void add(@NonNull View child) {
    if (child == null) {
        throw new IllegalArgumentException("view must be non-null");
    }
    if (child.getParent() instanceof ViewGroup) {
        ViewGroup parent = (ViewGroup) child.getParent();
        ...
        // 將child從原來(lái)的parent中remove掉
        parent.removeView(child);
        if (parent.getLayoutTransition() != null) {
            // LayoutTransition will cause the child to delay removal - cancel it
            parent.getLayoutTransition().cancel(LayoutTransition.DISAPPEARING);
        }
        // fail-safe if view is still attached for any reason
        if (child.getParent() != null) {
            child.mParent = null;
        }
    }
    super.addView(child);
}

用法也非常簡(jiǎn)單,我們來(lái)看看一個(gè)簡(jiǎn)單的demo

class OverlayActivity : AppCompatActivity() {
    private lateinit var binding: ActivityOverlayBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityOverlayBinding.inflate(LayoutInflater.from(this))
        setContentView(binding.root)
        addViewToOverlayView()
        addDrawableToOverlayView()
        binding.btClick.setOnClickListener {
        	// 測(cè)試一下OverlayView的動(dòng)畫
            TransitionManager.beginDelayedTransition(binding.llOverlayContainer, XYTranslation())
            binding.llOverlayContainer.translationX += 100
        }
    }
    private fun addViewToOverlayView() {
        val view = View(this)
        view.layoutParams = LinearLayout.LayoutParams(100, 100)
        view.setBackgroundColor(Color.parseColor("#ff00ff"))
        // 需要手動(dòng)調(diào)用layout,不然view顯示不出來(lái)
        view.layout(0, 0, 100, 100)
        binding.llOverlayContainer.overlay.add(view)
    }
    private fun addDrawableToOverlayView() {
        binding.view2.post {
            val drawable = ContextCompat.getDrawable(this, R.mipmap.ic_temp)
            // 需要手動(dòng)調(diào)用setBounds,不然drawable顯示不出來(lái)
            drawable!!.setBounds(binding.view2.width / 2, 0, binding.view2.width, binding.view2.height / 2)
            binding.view2.overlay.add(drawable)
        }
    }
}

效果圖如下:

這里唯一需要注意的是,如果是添加view,需要手動(dòng)調(diào)用layout布局,不然view顯示不出來(lái);如果添加的是drawable 需要手動(dòng)調(diào)用setBounds,不然drawable也顯示不出來(lái)

GhostView

GhostView Activity共享元素動(dòng)畫實(shí)現(xiàn)里比較重要的一個(gè)類,所以就單獨(dú)的介紹一下

它的作用是在不改變viewparent的情況下,將view繪制在另一個(gè)parent

我們先看看GhostView 的部分源碼

public class GhostView extends View {
    private final View mView;
    private int mReferences;
    private boolean mBeingMoved;
    private GhostView(View view) {
        super(view.getContext());
        mView = view;
        mView.mGhostView = this;
        final ViewGroup parent = (ViewGroup) mView.getParent();
        // 這句代碼 讓mView在原來(lái)的parent中隱藏(即不繪制視圖)
        mView.setTransitionVisibility(View.INVISIBLE);
        parent.invalidate();
    }
    @Override
    public void setVisibility(@Visibility int visibility) {
        super.setVisibility(visibility);
        if (mView.mGhostView == this) {
        	// 如果view在ghostview中繪制(可見(jiàn)),則設(shè)置在原來(lái)的parent不繪制(不可見(jiàn))
        	// 如果view在ghostview中不繪制(不可見(jiàn)),則設(shè)置在原來(lái)的parent繪制(可見(jiàn))
            int inverseVisibility = (visibility == View.VISIBLE) ? View.INVISIBLE : View.VISIBLE;
            mView.setTransitionVisibility(inverseVisibility);
        }
    }
}

看源碼得知 如果把View 添加到GhostView里,則默認(rèn)會(huì)調(diào)用viewsetTransitionVisibility方法 將view設(shè)置成在parent中不可見(jiàn), 在GhostView里可見(jiàn);調(diào)用GhostViewsetVisibility方法設(shè)置 要么在GhostView中可見(jiàn),要么在parent中可見(jiàn)

系統(tǒng)內(nèi)部是使用GhostView.addGhost靜態(tài)方法添加GhostView

我們來(lái)看看添加GhostViewaddGhost靜態(tài)方法源碼

public static GhostView addGhost(View view, ViewGroup viewGroup, Matrix matrix) {
    if (!(view.getParent() instanceof ViewGroup)) {
        throw new IllegalArgumentException("Ghosted views must be parented by a ViewGroup");
    }
    // 獲取 ViewGroupOverlay
    ViewGroupOverlay overlay = viewGroup.getOverlay();
    ViewOverlay.OverlayViewGroup overlayViewGroup = overlay.mOverlayViewGroup;
    GhostView ghostView = view.mGhostView;
    int previousRefCount = 0;
    if (ghostView != null) {
        View oldParent = (View) ghostView.getParent();
        ViewGroup oldGrandParent = (ViewGroup) oldParent.getParent();
        if (oldGrandParent != overlayViewGroup) {
            previousRefCount = ghostView.mReferences;
            oldGrandParent.removeView(oldParent);
            ghostView = null;
        }
    }
    if (ghostView == null) {
        if (matrix == null) {
            matrix = new Matrix();
            calculateMatrix(view, viewGroup, matrix);
        }
        // 創(chuàng)建GhostView
        ghostView = new GhostView(view);
        ghostView.setMatrix(matrix);
        FrameLayout parent = new FrameLayout(view.getContext());
        parent.setClipChildren(false);
        copySize(viewGroup, parent);
        // 設(shè)置GhostView的大小
        copySize(viewGroup, ghostView);
        // 將ghostView添加到了parent中
        parent.addView(ghostView);
        ArrayList<View> tempViews = new ArrayList<View>();
        int firstGhost = moveGhostViewsToTop(overlay.mOverlayViewGroup, tempViews);
        // 將parent添加到了ViewGroupOverlay中
        insertIntoOverlay(overlay.mOverlayViewGroup, parent, ghostView, tempViews, firstGhost);
        ghostView.mReferences = previousRefCount;
    } else if (matrix != null) {
        ghostView.setMatrix(matrix);
    }
    ghostView.mReferences++;
    return ghostView;
}

可見(jiàn)內(nèi)部的實(shí)現(xiàn)最終將GhostView添加到了ViewGroupOverlay(遮罩層)里

配合GhostView,同時(shí)也解決了ViewGroupOverlay會(huì)將viewparentremove的問(wèn)題(即可同時(shí)在ViewGroupOverlay和原來(lái)的parent中繪制)

我們來(lái)看看一個(gè)簡(jiǎn)單的demo

class GhostViewActivity : AppCompatActivity() {
    private lateinit var binding: ActivityGhostViewBinding
    private var ghostView: View? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityGhostViewBinding.inflate(LayoutInflater.from(this))
        setContentView(binding.root)
        binding.btClick.setOnClickListener {
        	// 配合動(dòng)畫看看效果
            TransitionManager.beginDelayedTransition(binding.llOverlayContainer, XYTranslation())
            binding.llOverlayContainer.translationX += 100
        }
        binding.btClick2.setOnClickListener {
            val ghostView = ghostView ?: return@setOnClickListener
           	// 測(cè)試一下ghostView的setVisibility方法效果
            if (ghostView.isVisible) {
                ghostView.visibility = View.INVISIBLE
            } else {
                ghostView.visibility = View.VISIBLE
            }
            (binding.view1.parent as ViewGroup).invalidate()
        }
        binding.view1.post {
        	// 創(chuàng)建一個(gè)GhostView添加到window.decorView的ViewGroupOverlay中
            ghostView = addGhost(binding.view1, window.decorView as ViewGroup)
        }
    }
	// 我們無(wú)法直接使用GhostView,只能臨時(shí)使用反射看看效果
    private fun addGhost(view: View, viewGroup: ViewGroup): View {
        val ghostViewClass = Class.forName("android.view.GhostView")
        val addGhostMethod: Method = ghostViewClass.getMethod(
            "addGhost", View::class.java,
            ViewGroup::class.java, Matrix::class.java
        )
        return addGhostMethod.invoke(null, view, viewGroup, null) as View
    }
}

效果圖如下:

可見(jiàn)使用GhostView并通過(guò)setVisibility方法,實(shí)現(xiàn)的效果是 既可以在window.decorViewViewGroupOverlay中繪制,也可以在原來(lái)的parent中繪制

那怎么同時(shí)繪制呢?

只需要在addGhost之后強(qiáng)制設(shè)置viewsetTransitionVisibilityView.VISIBLE即可

binding.view1.post {
    ghostView = addGhost(binding.view1, window.decorView as ViewGroup)
    // android 10 之前setTransitionVisibility是hide方法
    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.Q) {
        binding.view1.setTransitionVisibility(View.VISIBLE)
        (binding.view1.parent as ViewGroup).invalidate()
    }
}

效果圖如下:

Activity的共享元素源碼分析

好的,上面的準(zhǔn)備工作做完了之后,下面我們來(lái)真正的分析Activity的共享元素源碼

我們先以ActivityA打開(kāi)ActivityB為例

先是調(diào)用ActivityOptions.makeSceneTransitionAnimation創(chuàng)建包含共享元素的ActivityOptions對(duì)象

//android.app.ActivityOptions類的源碼
public class ActivityOptions {
	...
	public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
	        Pair&lt;View, String&gt;... sharedElements) {
	    ActivityOptions opts = new ActivityOptions();
	    // activity.mExitTransitionListener是SharedElementCallback對(duì)象
	    makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
	            activity.mExitTransitionListener, sharedElements);
	    return opts;
	}
}

其中activitymExitTransitionListenerSharedElementCallback對(duì)象,默認(rèn)值是SharedElementCallback.NULL_CALLBACK,使用的是默認(rèn)實(shí)現(xiàn);可以調(diào)用ActivitysetExitSharedElementCallback方法設(shè)置這個(gè)對(duì)象, 但是大多數(shù)情況下用默認(rèn)的即可

下面我們來(lái)簡(jiǎn)單介紹下SharedElementCallback的一些回調(diào)在什么情況下觸發(fā)

public abstract class SharedElementCallback {
	...
    static final SharedElementCallback NULL_CALLBACK = new SharedElementCallback() {
    };
    /**
    * 共享元素 開(kāi)始幀準(zhǔn)備好了 觸發(fā)
    * @param sharedElementNames 共享元素名稱
    * @param sharedElements 共享元素view,并且已經(jīng)將開(kāi)始幀的屬性應(yīng)用到view里了
    * @param sharedElementSnapshots 調(diào)用SharedElementCallback.onCreateSnapshotView方法創(chuàng)建的快照
    **/
    public void onSharedElementStart(List<String> sharedElementNames,
            List<View> sharedElements, List<View> sharedElementSnapshots) {}
    /**
    * 共享元素 結(jié)束幀準(zhǔn)備好了 觸發(fā)
    * @param sharedElementNames 共享元素名稱
    * @param sharedElements 共享元素view,并且已經(jīng)將結(jié)束幀的屬性應(yīng)用到view里了
    * @param sharedElementSnapshots 調(diào)用SharedElementCallback.onCreateSnapshotView方法創(chuàng)建的快照
    * 		注意:跟onSharedElementStart方法的sharedElementSnapshots參數(shù)是同一個(gè)對(duì)象
    */
    public void onSharedElementEnd(List<String> sharedElementNames,
        List<View> sharedElements, List<View> sharedElementSnapshots) {}
    /**
    * 比如在ActivityA存在,而在ActivityB不存在的共享元素 回調(diào)
    * @param rejectedSharedElements 在ActivityB中不存在的共享元素
    **/
    public void onRejectSharedElements(List<View> rejectedSharedElements) {}
    /**
    * 需要做動(dòng)畫的共享元素映射關(guān)系準(zhǔn)備好之后 回調(diào)
    * @param names 支持的所有共享元素名稱(是ActivityA打開(kāi)ActivityB時(shí)傳過(guò)來(lái)的所有共享元素名稱)
    * @param sharedElements 需要做動(dòng)畫的共享元素名稱及view的對(duì)應(yīng)關(guān)系
    *	 	注意:比如ActivityA打開(kāi)ActivityB,對(duì)于ActivityA中的回調(diào) names和sharedElements的大小基本上是一樣的,ActivityB中的回調(diào)就可能會(huì)不一樣
    **/
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
    /**
    * 將共享元素 view 生成 bitmap 保存在Parcelable中,最終這個(gè)Parcelable會(huì)保存在sharedElementBundle中
    * 如果是ActivityA打開(kāi)ActivityB, 則會(huì)把sharedElementBundle傳給ActivityB
    **/
    public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
        RectF screenBounds) {
        ...
     }
     /**
     * 根據(jù)snapshot反過(guò)來(lái)創(chuàng)建view
     * 如果是ActivityA打開(kāi)ActivityB, ActivityB接收到Parcelable對(duì)象后,在適當(dāng)?shù)臅r(shí)候會(huì)調(diào)用這個(gè)方法創(chuàng)建出view對(duì)象
     **/
    public View onCreateSnapshotView(Context context, Parcelable snapshot) {
            ...
    }
    /**
    * 當(dāng)共享元素和sharedElementBundle對(duì)象都已經(jīng)傳第給對(duì)方的時(shí)候觸發(fā)(表明接下來(lái)可以準(zhǔn)備執(zhí)行過(guò)場(chǎng)動(dòng)畫了)
    * 比如: ActivityA 打開(kāi) ActivityB, ActivityA調(diào)用完onCaptureSharedElementSnapshot將信息保存在sharedElementBundle中,然后傳給ActivityB,這個(gè)時(shí)候ActivityA 和 ActivityB的SharedElementCallback都會(huì)觸發(fā)onSharedElementsArrived方法
    **/
    public void onSharedElementsArrived(List<String> sharedElementNames,
        List<View> sharedElements, OnSharedElementsReadyListener listener) {
        listener.onSharedElementsReady();
    }
}

SharedElementCallback的每個(gè)回調(diào)方法的大致意思是這樣的

接下來(lái)我門繼續(xù)往下看源碼 makeSceneTransitionAnimation

//android.app.ActivityOptions類的源碼
public class ActivityOptions {
	...
	static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
            ActivityOptions opts, SharedElementCallback callback,
            Pair<View, String>[] sharedElements) {
        // activity的window一定要添加Window.FEATURE_ACTIVITY_TRANSITIONS特征
        if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
            opts.mAnimationType = ANIM_DEFAULT;
            return null;
        }
        opts.mAnimationType = ANIM_SCENE_TRANSITION;
        ArrayList<String> names = new ArrayList<String>();
        ArrayList<View> views = new ArrayList<View>();
        if (sharedElements != null) {
            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)建ActivityA退出時(shí)的過(guò)場(chǎng)動(dòng)畫核心類
        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
                callback, names, names, views, false);
        //注意 這個(gè)opts保存了ActivityA的exit對(duì)象,到時(shí)候會(huì)傳給ActivityB的EnterTransitionCoordinator對(duì)象
        opts.mTransitionReceiver = exit;
        // 支持的共享元素名稱
        opts.mSharedElementNames = names;
        // 是否是返回
        opts.mIsReturning = (activity == null);
        if (activity == null) {
            opts.mExitCoordinatorIndex = -1;
        } else {
        	// 將exit添加到mActivityTransitionState對(duì)象中,然后由ActivityTransitionState對(duì)象管理和調(diào)用exit對(duì)象里的方法
            opts.mExitCoordinatorIndex =
                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
        }
        return exit;
    }
}

接下來(lái)我們來(lái)看看ExitTransitionCoordinator這個(gè)類的構(gòu)造函數(shù)干了啥

// android.app.ActivityTransitionCoordinator源碼
abstract class ActivityTransitionCoordinator extends ResultReceiver {
    ...
    public ActivityTransitionCoordinator(Window window,
            ArrayList<String> allSharedElementNames,
            SharedElementCallback listener, boolean isReturning) {
        super(new Handler());
        mWindow = window;
        // activity里的SharedElementCallback對(duì)象
        mListener = listener;
        // 支持的所有共享元素名稱
        // 比如ActivityA打開(kāi)ActivityB,則是makeSceneTransitionAnimation方法傳過(guò)來(lái)的共享元素名稱
        mAllSharedElementNames = allSharedElementNames;
        // 是否是返回
        mIsReturning = isReturning;
    }
}
// android.app.ExitTransitionCoordinator源碼
public class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
	public ExitTransitionCoordinator(ExitTransitionCallbacks exitCallbacks,
            Window window, SharedElementCallback listener, ArrayList<String> names,
            ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
        super(window, names, listener, isReturning);
        // viewsReady主要有以下作用
        // 1. 準(zhǔn)備好需要執(zhí)行動(dòng)畫的共享元素,并排序 保存在mSharedElementNames和mSharedElements中
        // 2. 準(zhǔn)備好需要做退出動(dòng)畫的非共享元素,保存在mTransitioningViews中
        // 3. 這里會(huì)觸發(fā) SharedElementCallback的onMapSharedElements回調(diào)
        viewsReady(mapSharedElements(accepted, mapped));
        // 將mTransitioningViews中的不在屏幕內(nèi)的非共享元素剔除掉
        stripOffscreenViews();
        mIsBackgroundReady = !isReturning;
        mExitCallbacks = exitCallbacks;
    }
}

這里比較重要的方法就是viewsReady方法,核心作用就是我上面說(shuō)的

// android.app.ActivityTransitionCoordinator源碼
protected void viewsReady(ArrayMap<String, View> sharedElements) {
    // 剔除掉不在mAllSharedElementNames中共享元素
    sharedElements.retainAll(mAllSharedElementNames);
    if (mListener != null) {
        // 執(zhí)行SharedElementCallback的onMapSharedElements回調(diào)
        mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
    }
    // 共享元素排序
    setSharedElements(sharedElements);
    if (getViewsTransition() != null && mTransitioningViews != null) {
        ViewGroup decorView = getDecor();
        if (decorView != null) {
            // 遍歷decorView收集非共享元素
            decorView.captureTransitioningViews(mTransitioningViews);
        }
        // 移除掉其中的共享元素
        mTransitioningViews.removeAll(mSharedElements);
    }
    setEpicenter();
}

準(zhǔn)備好ActivityOptions參數(shù)后,就可以調(diào)用startActivity(Intent intent, @Nullable Bundle options)方法了,然后就會(huì)調(diào)用到activitycancelInputsAndStartExitTransition方法

// android.app.Activity源碼
private void cancelInputsAndStartExitTransition(Bundle options) {
    final View decor = mWindow != null ? mWindow.peekDecorView() : null;
    if (decor != null) {
        decor.cancelPendingInputEvents();
    }
    if (options != null) {
        // 開(kāi)始處理ActivityA的退場(chǎng)動(dòng)畫
        mActivityTransitionState.startExitOutTransition(this, options);
    }
}
// android.app.ActivityTransitionState源碼
public void startExitOutTransition(Activity activity, Bundle options) {
    mEnterTransitionCoordinator = null;
    if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
            mExitTransitionCoordinators == null) {
        return;
    }
    ActivityOptions activityOptions = new ActivityOptions(options);
    if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
        int key = activityOptions.getExitCoordinatorKey();
        int index = mExitTransitionCoordinators.indexOfKey(key);
        if (index >= 0) {
            mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
            mExitTransitionCoordinators.removeAt(index);
            if (mCalledExitCoordinator != null) {
                mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
                mExitingTo = mCalledExitCoordinator.getMappedNames();
                mExitingToView = mCalledExitCoordinator.copyMappedViews();
                // 調(diào)用ExitTransitionCoordinator的startExit
                mCalledExitCoordinator.startExit();
            }
        }
    }
}

這里startExitOutTransition里面就會(huì)調(diào)用ExitTransitionCoordinatorstartExit方法

// android.app.ExitTransitionCoordinator源碼
public void startExit() {
    if (!mIsExitStarted) {
        backgroundAnimatorComplete();
        mIsExitStarted = true;
        pauseInput();
        ViewGroup decorView = getDecor();
        if (decorView != null) {
            decorView.suppressLayout(true);
        }
        // 將共享元素用GhostView包裹,然后添加的Activity的decorView的OverlayView中
        moveSharedElementsToOverlay();
        startTransition(this::beginTransitions);
    }
}

這里的moveSharedElementsToOverlay方法比較重要,會(huì)使用到最開(kāi)始介紹的GhostViewOverlayView ,目的是將共享元素繪制到最頂層

然后開(kāi)始執(zhí)行beginTransitions方法

// android.app.ExitTransitionCoordinator源碼
private void beginTransitions() {
	// 獲取共享元素的過(guò)渡動(dòng)畫類Transition,可以通過(guò)window.setSharedElementExitTransition方法設(shè)置
	// 一般不需要設(shè)置 有默認(rèn)值
    Transition sharedElementTransition = getSharedElementExitTransition();
    // 獲取非共享元素的過(guò)渡動(dòng)畫類Transition,也可以通過(guò)window.setExitTransition方法設(shè)置
    Transition viewsTransition = getExitTransition();
	// 將sharedElementTransition和viewsTransition合并成一個(gè) TransitionSet
    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);
        }
        // 開(kāi)始采集開(kāi)始幀和結(jié)束幀,執(zhí)行過(guò)度動(dòng)畫
        TransitionManager.beginDelayedTransition(decorView, transition);
        scheduleGhostVisibilityChange(View.VISIBLE);
        setGhostVisibility(View.VISIBLE);
        if (viewsTransition != null) {
            setTransitioningViewsVisiblity(View.INVISIBLE, false);
        }
        decorView.invalidate();
    } else {
        transitionStarted();
    }
}

這里在TransitionManager.beginDelayedTransition的前后都有屌用setGhostVisibilityscheduleGhostVisibilityChange方法,是為了采集前后幀的屬性,執(zhí)行過(guò)度動(dòng)畫,采集完成之后,會(huì)顯示GhostView,而隱藏原來(lái)parent里的共享元素view

上面的sharedElementTransitionviewsTransition都添加了監(jiān)聽(tīng)器,在動(dòng)畫結(jié)束之后分別調(diào)用sharedElementTransitionCompleteviewsTransitionComplete方法

// android.app.ExitTransitionCoordinator源碼
@Override
protected void sharedElementTransitionComplete() {
    // 這里就會(huì)采集共享元素當(dāng)前的屬性(大小、位置等),會(huì)觸發(fā)SharedElementCallback.onCaptureSharedElementSnapshot方法
    mSharedElementBundle = mExitSharedElementBundle == null
            ? captureSharedElementState() : captureExitSharedElementsState();
    super.sharedElementTransitionComplete();
}
// android.app.ActivityTransitionCoordinator源碼
protected void viewsTransitionComplete() {
    mViewsTransitionComplete = true;
    startInputWhenTransitionsComplete();
}

然后在startInputWhenTransitionsComplete方法里會(huì)調(diào)用onTransitionsComplete方法,最終會(huì)調(diào)用notifyComplete方法

// android.app.ExitTransitionCoordinator源碼
protected boolean isReadyToNotify() {
    // 1. 調(diào)用完sharedElementTransitionComplete后,mSharedElementBundle不為null
    // 2. mResultReceiver是在ActivityB創(chuàng)建完EnterTransitionCoordinator之后,發(fā)送MSG_SET_REMOTE_RECEIVER消息 將EnterTransitionCoordinator傳給ActivityA之后不為null
    // 3. 看構(gòu)造函數(shù),一開(kāi)始就為true
    return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}
protected void notifyComplete() {
    if (isReadyToNotify()) {
        if (!mSharedElementNotified) {
            mSharedElementNotified = true;
            // 延遲發(fā)送一個(gè)MSG_CANCEL消息,清空各種狀態(tài)等
            delayCancel();
            if (!mActivity.isTopOfTask()) {
            	//  mResultReceiver是ActivityB的EnterTransitionCoordinator對(duì)象
                mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
            }
            if (mListener == null) {
                mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
                notifyExitComplete();
            } else {
                final ResultReceiver resultReceiver = mResultReceiver;
                final Bundle sharedElementBundle = mSharedElementBundle;
                // 觸發(fā)SharedElementCallback.onSharedElementsArrived
                mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements,
                        new OnSharedElementsReadyListener() {
                            @Override
                            public void onSharedElementsReady() {
                            	// 發(fā)送MSG_TAKE_SHARED_ELEMENTS,將共享元素的sharedElementBundle信息傳遞給ActivityB
                                resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS,
                                        sharedElementBundle);
                                notifyExitComplete();
                            }
                        });
            }
        } else {
            notifyExitComplete();
        }
    }
}

這里的notifyComplete會(huì)在特定的條件下不斷觸發(fā),一旦isReadyToNotifytrue,就會(huì)執(zhí)行方法里的邏輯

這里可能比較關(guān)心的是resultReceiver到底是什么對(duì)象,是怎么賦值的???(這里在接下來(lái)講到ActivityB的時(shí)候會(huì)介紹到)

ActivityA的流程暫時(shí)到這里就沒(méi)發(fā)走下去了

接下來(lái)我們來(lái)看看ActivityB, 當(dāng)打開(kāi)了ActivityB的時(shí)候會(huì)執(zhí)行ActivityperformStart方法

// android.app.Activity源碼
final void performStart(String reason) {
    dispatchActivityPreStarted();
    // getActivityOptions() 獲取到的是在上面ActivityA中創(chuàng)建的ActivityOptions對(duì)象
    // 里面有支持的所有的共享元素名稱、ActivityA的ExitTransitionCoordinator對(duì)象、返回標(biāo)志等信息
    mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    mFragments.noteStateNotSaved();
    mCalled = false;
    mFragments.execPendingActions();
    mInstrumentation.callActivityOnStart(this);
    EventLogTags.writeWmOnStartCalled(mIdent, getComponentName().getClassName(), reason);
 	...
    mActivityTransitionState.enterReady(this);
    dispatchActivityPostStarted();
}

然后就進(jìn)入到ActivityTransitionStateenterReady方法

// android.app.ActivityTransitionState源碼
public void enterReady(Activity activity) {
    if (mEnterActivityOptions == null || mIsEnterTriggered) {
        return;
    }
    mIsEnterTriggered = true;
    mHasExited = false;
    // 獲取ActivityA傳過(guò)來(lái)的所有共享元素名稱
    ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
    // 獲取ActivityA的ExitTransitionCoordinator
    ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
    // 獲取返回標(biāo)志
    final boolean isReturning = mEnterActivityOptions.isReturning();
    if (isReturning) {
        restoreExitedViews();
        activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
    }
    // 創(chuàng)建EnterTransitionCoordinator,保存resultReceiver、sharedElementNames等對(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) { // 是否推遲執(zhí)行動(dòng)畫,配合postponeEnterTransition方法使用
        startEnter();
    }
}

上面的mIsEnterPostponed,默認(rèn)值是false,可以通過(guò)postponeEnterTransitionstartPostponedEnterTransition控制什么時(shí)候執(zhí)行動(dòng)畫,這個(gè)不是重點(diǎn),我們忽略

我們先來(lái)看看EnterTransitionCoordinator的構(gòu)造函數(shù)

// android.app.EnterTransitionCoordinator源碼
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;
    // 保存ActivityA的ExitTransitionCoordinator對(duì)象到mResultReceiver中
    setResultReceiver(resultReceiver);
    // 這里會(huì)將ActivityB的window背景設(shè)置成透明
    prepareEnter();
    // 構(gòu)造resultReceiverBundle,保存EnterTransitionCoordinator對(duì)象
    Bundle resultReceiverBundle = new Bundle();
    resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
    // 發(fā)送MSG_SET_REMOTE_RECEIVER消息,將EnterTransitionCoordinator對(duì)象傳遞給ActivityA
    mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
    ...
}

這個(gè)時(shí)候ActivityA那邊就接收到了ActivityBEnterTransitionCoordinator對(duì)象

接下來(lái)我門看看ActivityA是怎么接收MSG_SET_REMOTE_RECEIVER消息的

// android.app.ExitTransitionCoordinator 源碼
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
    switch (resultCode) {
        case MSG_SET_REMOTE_RECEIVER:
            stopCancel();
            // 將`ActivityB`的`EnterTransitionCoordinator`對(duì)象保存到mResultReceiver對(duì)象中
            mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
            if (mIsCanceled) {
                mResultReceiver.send(MSG_CANCEL, null);
                mResultReceiver = null;
            } else {
            	//又會(huì)觸發(fā)notifyComplete(),這個(gè)時(shí)候isReadyToNotify就為true了,就會(huì)執(zhí)行notifyComplete里的代碼
                notifyComplete();
            }
            break;
        ...
    }
}

這個(gè)時(shí)候ActivityA的共享元素動(dòng)畫的核心邏輯就基本已經(jīng)走完了

接下來(lái)繼續(xù)看ActivityB的邏輯,接來(lái)下會(huì)執(zhí)行startEnter方法

// android.app.ActivityTransitionState源碼
private void startEnter() {
    if (mEnterTransitionCoordinator.isReturning()) { // 這個(gè)為false
        if (mExitingToView != null) {
            mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
                    mExitingToView);
        } else {
            mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
        }
    } else {
    	// 會(huì)執(zhí)行這個(gè)邏輯
        mEnterTransitionCoordinator.namedViewsReady(null, null);
        mPendingExitNames = null;
    }
    mExitingFrom = null;
    mExitingTo = null;
    mExitingToView = null;
    mEnterActivityOptions = null;
}

也就是說(shuō)接下來(lái)會(huì)觸發(fā)EnterTransitionCoordinatornamedViewsReady方法, 然后就會(huì)觸發(fā)viewsReady方法

// android.app.EnterTransitionCoordinator源碼
public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
    triggerViewsReady(mapNamedElements(accepted, localNames));
}
// android.app.EnterTransitionCoordinator源碼
private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
    if (mAreViewsReady) {
        return;
    }
    mAreViewsReady = true;
    final ViewGroup decor = getDecor();
    // Ensure the views have been laid out before capturing the views -- we need the epicenter.
    if (decor == null || (decor.isAttachedToWindow() &&
            (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
        viewsReady(sharedElements);
    } else {
        mViewsReadyListener = OneShotPreDrawListener.add(decor, () -> {
            mViewsReadyListener = null;
            // 觸發(fā)viewsReady
            viewsReady(sharedElements);
        });
        decor.invalidate();
    }
}

EnterTransitionCoordinatorviewsReady代碼邏輯 跟 ExitTransitionCoordinator的差不多,準(zhǔn)備好共享元素和非共享元素等,

// android.app.EnterTransitionCoordinator源碼
@Override
protected void viewsReady(ArrayMap<String, View> sharedElements) {
    // 準(zhǔn)備好共享元素和非共享元素
    super.viewsReady(sharedElements);
    mIsReadyForTransition = true;
    // 隱藏共享元素
    hideViews(mSharedElements);
    Transition viewsTransition = getViewsTransition();
    if (viewsTransition != null && mTransitioningViews != null) {
    	// 將mTransitioningViews當(dāng)作target設(shè)置到viewsTransition中
        removeExcludedViews(viewsTransition, mTransitioningViews);
        // 剔除掉mTransitioningViews中不在屏幕中的view
        stripOffscreenViews();
        // 隱藏非共享元素
        hideViews(mTransitioningViews);
    }
    if (mIsReturning) {
        sendSharedElementDestination();
    } else {
    	// 將共享元素用GhostView包裹,然后添加到ActivityB的decorView的OverlayView中
        moveSharedElementsToOverlay();
    }
    if (mSharedElementsBundle != null) {
        onTakeSharedElements();
    }
}

一般情況下,這個(gè)時(shí)候mSharedElementsBundle為null,所以不會(huì)走onTakeSharedElements方法

這里的mSharedElementsBundle對(duì)象是在ActivityA的notifyComplete中發(fā)送的MSG_TAKE_SHARED_ELEMENTS消息傳過(guò)來(lái)的

// android.app.EnterTransitionCoordinator源碼
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
    switch (resultCode) {
        case MSG_TAKE_SHARED_ELEMENTS:
            if (!mIsCanceled) {
                mSharedElementsBundle = resultData;
                onTakeSharedElements();
            }
            break;
        ...
    }
}

可見(jiàn)當(dāng)ActivityB接收到MSG_TAKE_SHARED_ELEMENTS消息,賦值完mSharedElementsBundle之后,也會(huì)執(zhí)行onTakeSharedElements方法

接下來(lái)我們來(lái)看看onTakeSharedElements方法

// android.app.EnterTransitionCoordinator源碼
private void onTakeSharedElements() {
    if (!mIsReadyForTransition || mSharedElementsBundle == null) {
        return;
    }
    final Bundle sharedElementState = mSharedElementsBundle;
    mSharedElementsBundle = null;
    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 {
    	// 觸發(fā)SharedElementCallback.onSharedElementsArrived回調(diào)
        mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements, listener);
    }
}

接下來(lái)就會(huì)執(zhí)行startTransition方法,然后執(zhí)行startSharedElementTransition方法,開(kāi)始執(zhí)行ActivityB的動(dòng)畫了

//  android.app.EnterTransitionCoordinator源碼
private void startSharedElementTransition(Bundle sharedElementState) {
    ViewGroup decorView = getDecor();
    if (decorView == null) {
        return;
    }
    // Remove rejected shared elements
    ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
    // 過(guò)濾出ActivityA存在,ActivityB不存在的共享元素
    rejectedNames.removeAll(mSharedElementNames);
    // 根據(jù)ActivityA傳過(guò)來(lái)的共享元素sharedElementState信息,創(chuàng)建快照view對(duì)象
    // 這里會(huì)觸發(fā)SharedElementCallback.onCreateSnapshotView方法
    ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
    if (mListener != null) {
    	// 觸發(fā)SharedElementCallback.onRejectSharedElements方法
        mListener.onRejectSharedElements(rejectedSnapshots);
    }
    removeNullViews(rejectedSnapshots);
    // 執(zhí)行漸隱的退出動(dòng)畫
    startRejectedAnimations(rejectedSnapshots);
    // 開(kāi)始創(chuàng)建共享元素的快照view
    // 這里會(huì)再觸發(fā)一遍SharedElementCallback.onCreateSnapshotView方法
    ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
            mSharedElementNames);
    // 顯示共享元素
    showViews(mSharedElements, true);
    // 添加OnPreDrawListener,在下一幀觸發(fā)SharedElementCallback.onSharedElementEnd回調(diào)
    scheduleSetSharedElementEnd(sharedElementSnapshots);
    // 設(shè)置共享元素設(shè)置到動(dòng)畫的開(kāi)始位置,并返回在ActivityB布局中的原始的狀態(tài)(即結(jié)束位置)
    // 這里會(huì)觸發(fā)SharedElementCallback.onSharedElementStart回調(diào)
    ArrayList<SharedElementOriginalState> originalImageViewState =
            setSharedElementState(sharedElementState, sharedElementSnapshots);
    requestLayoutForSharedElements();
    boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
    boolean startSharedElementTransition = true;
    setGhostVisibility(View.INVISIBLE);
    scheduleGhostVisibilityChange(View.INVISIBLE);
    pauseInput();
    // 然后就開(kāi)始采集開(kāi)始幀和結(jié)束幀,執(zhí)行過(guò)度動(dòng)畫
    Transition transition = beginTransition(decorView, startEnterTransition,
            startSharedElementTransition);
    scheduleGhostVisibilityChange(View.VISIBLE);
    setGhostVisibility(View.VISIBLE);
    if (startEnterTransition) {
    	// 添加監(jiān)聽(tīng)器,動(dòng)畫結(jié)束的時(shí)候,將window的背景恢復(fù)成不透明等
        startEnterTransition(transition);
    }
    // 將共享元素設(shè)置到結(jié)束的位置(為了TransitionManager能采集到結(jié)束幀的值)
    setOriginalSharedElementState(mSharedElements, originalImageViewState);
    if (mResultReceiver != null) {
        // We can't trust that the view will disappear on the same frame that the shared
        // element appears here. Assure that we get at least 2 frames for double-buffering.
        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.
                }
            }
        });
    }
}

接下來(lái)看一下beginTransition方法

// android.app.EnterTransitionCoordinator源碼
private Transition beginTransition(ViewGroup decorView, boolean startEnterTransition,
        boolean startSharedElementTransition) {
    Transition sharedElementTransition = null;
    if (startSharedElementTransition) {
        if (!mSharedElementNames.isEmpty()) {
            // 獲取共享元素的過(guò)渡動(dòng)畫類Transition,可以通過(guò)window.setSharedElementEnterTransition方法設(shè)置
            // 一般不需要設(shè)置 有默認(rèn)值
            sharedElementTransition = configureTransition(getSharedElementTransition(), false);
        }
        ...
    }
    Transition viewsTransition = null;
    if (startEnterTransition) {
        mIsViewsTransitionStarted = true;
        if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
            // 獲取非共享元素的過(guò)渡動(dòng)畫類Transition,可以通過(guò)window.setEnterTransition方法設(shè)置
            // 一般不需要設(shè)置 有默認(rèn)值
            viewsTransition = configureTransition(getViewsTransition(), true);
        }
        ...
    // 合并成TransitionSet 對(duì)象
    Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
    if (transition != null) {
        transition.addListener(new ContinueTransitionListener());
        if (startEnterTransition) {
            setTransitioningViewsVisiblity(View.INVISIBLE, false);
        }
        // 開(kāi)始采集開(kāi)始幀和結(jié)束幀,執(zhí)行過(guò)度動(dòng)畫
        TransitionManager.beginDelayedTransition(decorView, transition);
        if (startEnterTransition) {
            setTransitioningViewsVisiblity(View.VISIBLE, false);
        }
        decorView.invalidate();
    } else {
        transitionStarted();
    }
    return transition;
}

到了這里,就會(huì)真正的開(kāi)始執(zhí)行 ActivityB的共享元素和非共享元素的進(jìn)場(chǎng)動(dòng)畫

當(dāng)動(dòng)畫執(zhí)行結(jié)束之后就會(huì)觸發(fā) onTransitionsComplete方法

// android.app.EnterTransitionCoordinator源碼
@Override
protected void onTransitionsComplete() {
    // 將共享元素和GhostView從decorView的OverlayView中remove掉
    moveSharedElementsFromOverlay();
    final ViewGroup decorView = getDecor();
    if (decorView != null) {
        decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
        Window window = getWindow();
        if (window != null && mReplacedBackground == decorView.getBackground()) {
            window.setBackgroundDrawable(null);
        }
    }
    if (mOnTransitionComplete != null) {
        mOnTransitionComplete.run();
        mOnTransitionComplete = null;
    }
}

用非常簡(jiǎn)單點(diǎn)的話總結(jié)共享元素流程是:

  • ActivityA先執(zhí)行退場(chǎng)動(dòng)畫
  • ActivityA將共享元素的結(jié)束位置等屬性傳遞給ActivityB
  • ActivityB加載自己的布局,在onStart生命周期左右去找到共享元素 先定位到ActivityA的結(jié)束位置
  • ActivityB開(kāi)始執(zhí)行過(guò)度動(dòng)畫,過(guò)渡到自己布局中的位置

這就是 從ActivityA打開(kāi)ActivityB的共享元素動(dòng)畫過(guò)程的核心源碼分析過(guò)程

ActivityB返回ActivityA

既然是返回,首先肯定是要調(diào)用ActivityBfinishAfterTransition方法

// android.app.Activity 源碼
public void finishAfterTransition() {
    if (!mActivityTransitionState.startExitBackTransition(this)) {
        finish();
    }
}

這里就會(huì)調(diào)用ActivityTransitionStatestartExitBackTransition方法

// android.app.ActivityTransitionState源碼
public boolean startExitBackTransition(final Activity activity) {
	// 獲取打開(kāi)ActivityB時(shí) 傳過(guò)來(lái)的共享元素名稱
    ArrayList<String> pendingExitNames = getPendingExitNames();
    if (pendingExitNames == null || mCalledExitCoordinator != null) {
        return false;
    } else {
        if (!mHasExited) {
            mHasExited = true;
            Transition enterViewsTransition = null;
            ViewGroup decor = null;
            boolean delayExitBack = false;
           ...
           // 創(chuàng)建ExitTransitionCoordinator對(duì)象
            mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
                    activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames,
                    null, null, true);
            if (enterViewsTransition != null && decor != null) {
                enterViewsTransition.resume(decor);
            }
            if (delayExitBack && decor != null) {
                final ViewGroup finalDecor = decor;
                // 在下一幀調(diào)用startExit方法
                OneShotPreDrawListener.add(decor, () -> {
                    if (mReturnExitCoordinator != null) {
                        mReturnExitCoordinator.startExit(activity.mResultCode,
                                activity.mResultData);
                    }
                });
            } else {
                mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
            }
        }
        return true;
    }
}

這個(gè)方法首先會(huì)獲取到需要執(zhí)行退場(chǎng)動(dòng)畫的共享元素(由ActivityA打開(kāi)ActivityB時(shí)傳過(guò)來(lái)的),然后會(huì)創(chuàng)建ExitTransitionCoordinator對(duì)象,最后調(diào)用startExit 執(zhí)行ActivityB的退場(chǎng)邏輯

我們繼續(xù)看看ExitTransitionCoordinator的構(gòu)造方法,雖然在上面在分析ActivityA打開(kāi)ActivityB時(shí)已經(jīng)看過(guò)了這個(gè)構(gòu)造方法,但ActivityB返回ActivityA時(shí)有點(diǎn)不一樣,acceptedmapped參數(shù)為null,isReturning參數(shù)為true

// android.app.ExitTransitionCoordinator源碼
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不一樣了
    viewsReady(mapSharedElements(accepted, mapped));
    // 剔除掉mTransitioningViews中不在屏幕內(nèi)的非共享元素
    stripOffscreenViews();
    mIsBackgroundReady = !isReturning;
    mActivity = activity;
}
// android.app.ActivityTransitionCoordinator源碼
protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
        ArrayList<View> localViews) {
    ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
    if (accepted != null) {
        for (int i = 0; i < accepted.size(); i++) {
            sharedElements.put(accepted.get(i), localViews.get(i));
        }
    } else {
        ViewGroup decorView = getDecor();
        if (decorView != null) {
            // 遍歷整個(gè)ActivityB的所有view,找到所有設(shè)置了transitionName屬性的view
            decorView.findNamedViews(sharedElements);
        }
    }
    return sharedElements;
}

這里由于acceptedmapped參數(shù)為null,所以會(huì)遍歷整個(gè)decorView上的所有view,找到所有設(shè)置了transitionName屬性的view,保存到sharedElements

然后viewsReady就會(huì)根據(jù)自己所支持的共享元素名稱,從sharedElements中刪除所有不支持的共享元素,然后對(duì)其排序,并保存到mSharedElements(保存的view對(duì)象)和mSharedElementNames(保存的是共享元素名稱)中; 同時(shí)也會(huì)準(zhǔn)備好非共享元素view對(duì)象,保存在mTransitioningViews

注意viewReady會(huì)觸發(fā)SharedElementCallback.onMapSharedElements回調(diào)

結(jié)下來(lái)就會(huì)執(zhí)行ExitTransitionCoordinatorstartExit方法

// android.app.ExitTransitionCoordinator源碼
public void startExit(int resultCode, Intent data) {
    if (!mIsExitStarted) {
        ...
        // 這里又將ActivityB的共享元素用GhostView包裹一下,添加的decorView的OverlayView中
        moveSharedElementsToOverlay();
        // 將ActivityB的window背景設(shè)置成透明
        if (decorView != null && decorView.getBackground() == null) {
            getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        }
        final boolean targetsM = decorView == null || decorView.getContext()
                .getApplicationInfo().targetSdkVersion >= VERSION_CODES.M;
        ArrayList<String> sharedElementNames = targetsM ? mSharedElementNames :
                mAllSharedElementNames;
        //這里創(chuàng)建options對(duì)象,保存ExitTransitionCoordinator、sharedElementNames等對(duì)象
        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
                sharedElementNames, resultCode, data);
        // 這里會(huì)將ActivityB改成透明的activity,同時(shí)會(huì)將options對(duì)象傳給ActivityA
        mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
            @Override
            public void onTranslucentConversionComplete(boolean drawComplete) {
                if (!mIsCanceled) {
                    fadeOutBackground();
                }
            }
        }, options);
        startTransition(new Runnable() {
            @Override
            public void run() {
                startExitTransition();
            }
        });
    }
}

這個(gè)方法的主要作用是

  • 使用GhostView 將共享元素view添加到最頂層decorViewOverlayView
  • 然后創(chuàng)建一個(gè)ActivityOptions 對(duì)象,把ActivityBExitTransitionCoordinator對(duì)象和支持的共享元素集合對(duì)象傳遞給ActivityA
  • 將ActivityB改成透明背景

然后就會(huì)執(zhí)行startExitTransition方法

// android.app.ExitTransitionCoordinator源碼
private void startExitTransition() {
    // 獲取ActivityB的非共享元素退場(chǎng)的過(guò)渡動(dòng)畫Transition對(duì)象
    // 最終會(huì)調(diào)用getReturnTransition方法獲取Transition對(duì)象
    Transition transition = getExitTransition();
    ViewGroup decorView = getDecor();
    if (transition != null && decorView != null && mTransitioningViews != null) {
        setTransitioningViewsVisiblity(View.VISIBLE, false);
        // 開(kāi)始執(zhí)行非共享元素的退場(chǎng)動(dòng)畫
        TransitionManager.beginDelayedTransition(decorView, transition);
        setTransitioningViewsVisiblity(View.INVISIBLE, false);
        decorView.invalidate();
    } else {
        transitionStarted();
    }
}

看到這里我們就知道了,這里會(huì)單獨(dú)先執(zhí)行非共享元素的退場(chǎng)動(dòng)畫

ActivityB的退場(chǎng)流程暫時(shí)就走到這里了,結(jié)下來(lái)就需要ActivityA的配和,所以接下來(lái)我們來(lái)看看ActivityA的進(jìn)場(chǎng)邏輯

ActivityA進(jìn)場(chǎng)時(shí),會(huì)調(diào)用performStart方法

// android.app.Activity 源碼
final void performStart(String reason) {
    dispatchActivityPreStarted();
    // 這里的getActivityOptions()獲取到的就是上面說(shuō)的`ActivityB`傳過(guò)來(lái)的對(duì)象
    mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    mFragments.noteStateNotSaved();
    mCalled = false;
    mFragments.execPendingActions();
    mInstrumentation.callActivityOnStart(this);
    EventLogTags.writeWmOnStartCalled(mIdent, getComponentName().getClassName(), reason);
    ...
    // ActivityA準(zhǔn)備執(zhí)行進(jìn)場(chǎng)邏輯
    mActivityTransitionState.enterReady(this);
    dispatchActivityPostStarted();
}
// android.app.ActivityTransitionState 源碼
public void enterReady(Activity activity) {
    // mEnterActivityOptions對(duì)象就是`ActivityB`傳過(guò)來(lái)的對(duì)象
    if (mEnterActivityOptions == null || mIsEnterTriggered) {
        return;
    }
    mIsEnterTriggered = true;
    mHasExited = false;
    // 共享元素名稱
    ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
    // ActivityB的ExitTransitionCoordinator對(duì)象
    ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
    // 返回標(biāo)志 true
    final boolean isReturning = mEnterActivityOptions.isReturning();
    if (isReturning) {
        restoreExitedViews();
        activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
    }
    // 創(chuàng)建ActivityA的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();
    }
}

ActivityA進(jìn)場(chǎng)時(shí),會(huì)在performStart里獲取并保存ActivityB傳過(guò)來(lái)的對(duì)象,然后創(chuàng)建EnterTransitionCoordinator進(jìn)場(chǎng)動(dòng)畫實(shí)現(xiàn)的核心類,然后調(diào)用startEnter方法

// android.app.ActivityTransitionState 源碼
private void startEnter() {
    if (mEnterTransitionCoordinator.isReturning()) {
    	// 這里的mExitingFrom、mExitingTo、mExitingToView是ActivityA打開(kāi)ActivityB的時(shí)候保存下載的對(duì)象
    	// 所以一般情況下都不為null
        if (mExitingToView != null) {
            mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
                    mExitingToView);
        } else {
            mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
        }
    } else {
        mEnterTransitionCoordinator.namedViewsReady(null, null);
        mPendingExitNames = null;
    }
    mExitingFrom = null;
    mExitingTo = null;
    mExitingToView = null;
    mEnterActivityOptions = null;
}

接下來(lái)就會(huì)執(zhí)行EnterTransitionCoordinatorviewInstancesReady方法

// android.app.EnterTransitionCoordinator 源碼
public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
        ArrayList<View> localViews) {
    boolean remap = false;
    for (int i = 0; i < localViews.size(); i++) {
        View view = localViews.get(i);
        // view的TransitionName屬性有沒(méi)有發(fā)生變化,或者view對(duì)象沒(méi)有AttachedToWindow
        if (!TextUtils.equals(view.getTransitionName(), localNames.get(i))
                || !view.isAttachedToWindow()) {
            remap = true;
            break;
        }
    }
    if (remap) {// 如果發(fā)生了變化,則會(huì)調(diào)用mapNamedElements遍歷decorView找到所有設(shè)置了TransitionName的view
        triggerViewsReady(mapNamedElements(accepted, localNames));
    } else { // 一般會(huì)執(zhí)行這個(gè)else
        triggerViewsReady(mapSharedElements(accepted, localViews));
    }
}

接下來(lái)就會(huì)執(zhí)行 triggerViewsReady,里面就會(huì)調(diào)用viewsReady方法,viewsReady在上面介紹過(guò),唯一有點(diǎn)不一樣的是 這里的mIsReturningtrue, 所以會(huì)執(zhí)行sendSharedElementDestination方法

// android.app.EnterTransitionCoordinator源碼
@Override
protected void viewsReady(ArrayMap&lt;String, View&gt; sharedElements) {
    // 準(zhǔn)備好共享元素和非共享元素
    super.viewsReady(sharedElements);
    mIsReadyForTransition = true;
    // 隱藏共享元素
    hideViews(mSharedElements);
    Transition viewsTransition = getViewsTransition();
    if (viewsTransition != null &amp;&amp; mTransitioningViews != null) {
    	// 將mTransitioningViews當(dāng)作target設(shè)置到viewsTransition中
        removeExcludedViews(viewsTransition, mTransitioningViews);
        // 剔除掉mTransitioningViews中不在屏幕中的view
        stripOffscreenViews();
        // 隱藏非共享元素
        hideViews(mTransitioningViews);
    }
    if (mIsReturning) {
        sendSharedElementDestination();
    } else {
        moveSharedElementsToOverlay();
    }
    if (mSharedElementsBundle != null) {
        onTakeSharedElements();
    }
}
// android.app.EnterTransitionCoordinator源碼
private void sendSharedElementDestination() {
    boolean allReady;
    final View decorView = getDecor();
    if (allowOverlappingTransitions() && getEnterViewsTransition() != null) {
        allReady = false;
    } else if (decorView == null) {
        allReady = true;
    } else {
        allReady = !decorView.isLayoutRequested();
        if (allReady) {
            for (int i = 0; i < mSharedElements.size(); i++) {
                if (mSharedElements.get(i).isLayoutRequested()) {
                    allReady = false;
                    break;
                }
            }
        }
    }
    if (allReady) {
    	// 捕獲共享元素當(dāng)前的狀態(tài), 會(huì)觸發(fā)SharedElementCallback.onCaptureSharedElementSnapshot方法
        Bundle state = captureSharedElementState();
        // 將共享元素view 添加的最頂層(decorView的OverlayView中)
        moveSharedElementsToOverlay();
        // 給ActivityB發(fā)送MSG_SHARED_ELEMENT_DESTINATION,將共享元素的狀態(tài)傳給ActivityB
        mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
    } else if (decorView != null) {
        OneShotPreDrawListener.add(decorView, () -> {
            if (mResultReceiver != null) {
                Bundle state = captureSharedElementState();
                moveSharedElementsToOverlay();
                mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
            }
        });
    }
    if (allowOverlappingTransitions()) {
    	// 執(zhí)行非共享元素的進(jìn)場(chǎng)動(dòng)畫
        startEnterTransitionOnly();
    }
}

sendSharedElementDestination方法主要有以下三個(gè)作用

  • 獲取ActivityA當(dāng)前共享元素的狀態(tài) 傳給ActivityB,當(dāng)作過(guò)度動(dòng)畫結(jié)束位置的狀態(tài)(即結(jié)束幀)
  • 將共享元素添加到最頂層(decorView的OverlayView中)
  • ActivityB發(fā)送MSG_SHARED_ELEMENT_DESTINATION消息傳遞state
  • 優(yōu)先開(kāi)始執(zhí)行ActivityA的非共享元素的進(jìn)場(chǎng)動(dòng)畫

到這里ActivityA的邏輯暫時(shí)告一段落,接下來(lái)我們來(lái)看看ActivityB接收到MSG_SHARED_ELEMENT_DESTINATION時(shí)干了些什么

// android.app.ExitTransitionCoordinator
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
    switch (resultCode) {
        ...
        case MSG_SHARED_ELEMENT_DESTINATION:
            // 保存ActivityA傳過(guò)來(lái)的共享元素狀態(tài)
            mExitSharedElementBundle = resultData;
            // 準(zhǔn)備執(zhí)行共享元素退出動(dòng)畫
            sharedElementExitBack();
            break;
        ...
    }
}

接下來(lái)我們來(lái)看看sharedElementExitBack方法

// android.app.ExitTransitionCoordinator
private void sharedElementExitBack() {
    final ViewGroup decorView = getDecor();
    if (decorView != null) {
        decorView.suppressLayout(true);
    }
    if (decorView != null && mExitSharedElementBundle != null &&
            !mExitSharedElementBundle.isEmpty() &&
            !mSharedElements.isEmpty() && getSharedElementTransition() != null) {
        startTransition(new Runnable() {
            public void run() {
            	// 會(huì)執(zhí)行這個(gè)方法
                startSharedElementExit(decorView);
            }
        });
    } else {
        sharedElementTransitionComplete();
    }
}

接下來(lái)就會(huì)執(zhí)行startSharedElementExit方法

// android.app.ExitTransitionCoordinator
private void startSharedElementExit(final ViewGroup decorView) {
    // 獲取共享元素的過(guò)度動(dòng)畫的Transition對(duì)象,里面最終會(huì)調(diào)用`getSharedElementReturnTransition`方法獲取該對(duì)象
    Transition transition = getSharedElementExitTransition();
    transition.addListener(new TransitionListenerAdapter() {
        @Override
        public void onTransitionEnd(Transition transition) {
            transition.removeListener(this);
            if (isViewsTransitionComplete()) {
                delayCancel();
            }
        }
    });
    // 根據(jù)ActivityA傳過(guò)來(lái)的狀態(tài),創(chuàng)建快照view對(duì)象
    // 這里會(huì)觸發(fā)SharedElementCallback.onCreateSnapshotView方法
    final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
            mSharedElementNames);
    OneShotPreDrawListener.add(decorView, () -> {
    	// 在下一幀觸發(fā),將共享元素的屬性設(shè)置到開(kāi)始狀態(tài)(ActivityA中共享元素的狀態(tài))
    	// 這里會(huì)觸發(fā)SharedElementCallback.onSharedElementStart回調(diào)
        setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
    });
    setGhostVisibility(View.INVISIBLE);
    scheduleGhostVisibilityChange(View.INVISIBLE);
    if (mListener != null) {
    	// 先觸發(fā)SharedElementCallback.onSharedElementEnd回調(diào)
        mListener.onSharedElementEnd(mSharedElementNames, mSharedElements,
                sharedElementSnapshots);
    }
    // 采集開(kāi)始幀和結(jié)束幀,并執(zhí)行動(dòng)畫
    TransitionManager.beginDelayedTransition(decorView, transition);
    scheduleGhostVisibilityChange(View.VISIBLE);
    setGhostVisibility(View.VISIBLE);
    decorView.invalidate();
}

看到上面的方法你可能會(huì)發(fā)現(xiàn),先觸發(fā)了onSharedElementEnd方法,然后再觸發(fā)onSharedElementStart,這可能是因?yàn)?code>ActivityB返回到ActivityA時(shí), google工程師定義為是從結(jié)束狀態(tài)返回到開(kāi)始狀態(tài)吧,即ActivityB的狀態(tài)為結(jié)束狀態(tài),ActivityA的狀態(tài)為開(kāi)始狀態(tài)

至于setGhostVisibilityscheduleGhostVisibilityChange主要的作用是為TransitionManager采集開(kāi)始幀和結(jié)束幀執(zhí)行動(dòng)畫用的

到這里ActivityB就開(kāi)始執(zhí)行共享元素的退出動(dòng)畫了

當(dāng)ActivityB共享元素動(dòng)畫執(zhí)行結(jié)束之后,就會(huì)調(diào)用sharedElementTransitionComplete方法,然后就會(huì)調(diào)用notifyComplete方法

@Override
protected void sharedElementTransitionComplete() {
    // 這里又會(huì)獲取ActivityB共享元素的狀態(tài)(之后會(huì)傳給ActivityA)
    // 可能會(huì)觸發(fā)ActivityB的SharedElementCallback.onCaptureSharedElementSnapshot回調(diào)
    mSharedElementBundle = mExitSharedElementBundle == null
            ? captureSharedElementState() : captureExitSharedElementsState();
    super.sharedElementTransitionComplete();
}

這里為什么要再一次獲取ActivityB的共享元素的狀態(tài),因?yàn)樾枰獋鹘oActivityA, 然后ActivityA再根據(jù)條件判斷 共享元素的狀態(tài)是否又發(fā)生了變化,然后交給ActivityA自己去執(zhí)行共享元素動(dòng)畫

至于最后會(huì)執(zhí)行notifyComplete,源碼就沒(méi)什么好看的了,上面也都介紹過(guò),這里面主要是給ActivityA發(fā)送了MSG_TAKE_SHARED_ELEMENTS消息,將ActivityB的共享元素的狀態(tài)對(duì)象(mSharedElementBundle)傳遞給ActivityA

到這里ActivityB退場(chǎng)動(dòng)畫基本上就結(jié)束了,至于最后的狀態(tài)清空等處理 我們就不看了

接下來(lái)我們繼續(xù)看ActivityA接收到MSG_TAKE_SHARED_ELEMENTS消息后做了什么

@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
    switch (resultCode) {
        case MSG_TAKE_SHARED_ELEMENTS:
            if (!mIsCanceled) {
            	//  保存共享元素狀態(tài)對(duì)象
                mSharedElementsBundle = resultData;
                // 準(zhǔn)備執(zhí)行共享元素動(dòng)畫
                onTakeSharedElements();
            }
            break;
    	...
    }
}

結(jié)下來(lái)就會(huì)執(zhí)行onTakeSharedElements方法,這些方法的源碼上面都介紹過(guò),我就不在貼出來(lái)了,這里面會(huì)觸發(fā)SharedElementCallback.onSharedElementsArrived回調(diào),然后執(zhí)行startSharedElementTransition

//  android.app.EnterTransitionCoordinator源碼
private void startSharedElementTransition(Bundle sharedElementState) {
    ViewGroup decorView = getDecor();
    if (decorView == null) {
        return;
    }
    // Remove rejected shared elements
    ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
    // 過(guò)濾出ActivityB存在,ActivityA不存在的共享元素
    rejectedNames.removeAll(mSharedElementNames);
    // 根據(jù)ActivityB傳過(guò)來(lái)的共享元素sharedElementState信息,創(chuàng)建快照view對(duì)象
    // 這里會(huì)觸發(fā)SharedElementCallback.onCreateSnapshotView方法
    ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
    if (mListener != null) {
    	// 觸發(fā)SharedElementCallback.onRejectSharedElements方法
        mListener.onRejectSharedElements(rejectedSnapshots);
    }
    removeNullViews(rejectedSnapshots);
    // 執(zhí)行漸隱的退出動(dòng)畫
    startRejectedAnimations(rejectedSnapshots);
    // 開(kāi)始創(chuàng)建共享元素的快照view
    // 這里會(huì)再觸發(fā)一遍SharedElementCallback.onCreateSnapshotView方法
    ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
            mSharedElementNames);
    // 顯示共享元素
    showViews(mSharedElements, true);
    // 添加OnPreDrawListener,在下一幀觸發(fā)SharedElementCallback.onSharedElementEnd回調(diào)
    scheduleSetSharedElementEnd(sharedElementSnapshots);
    // 設(shè)置共享元素設(shè)置到動(dòng)畫的開(kāi)始位置,并返回在ActivityA布局中的原始的狀態(tài)(即結(jié)束位置)
    // SharedElementCallback.onSharedElementStart回調(diào)
    ArrayList<SharedElementOriginalState> originalImageViewState =
            setSharedElementState(sharedElementState, sharedElementSnapshots);
    requestLayoutForSharedElements();
    boolean startEnterTransition = allowOverlappingTransitions() && !mIsReturning;
    boolean startSharedElementTransition = true;
    setGhostVisibility(View.INVISIBLE);
    scheduleGhostVisibilityChange(View.INVISIBLE);
    pauseInput();
    // 然后就開(kāi)始采集開(kāi)始幀和結(jié)束幀,執(zhí)行過(guò)度動(dòng)畫
    Transition transition = beginTransition(decorView, startEnterTransition,
            startSharedElementTransition);
    scheduleGhostVisibilityChange(View.VISIBLE);
    setGhostVisibility(View.VISIBLE);
    if (startEnterTransition) {// 這里為false,不會(huì)執(zhí)行, 因?yàn)榉枪蚕碓貏?dòng)畫執(zhí)行單獨(dú)執(zhí)行了
        startEnterTransition(transition);
    }
    // 將共享元素設(shè)置到結(jié)束的位置(為了TransitionManager能采集到結(jié)束幀的值)
    setOriginalSharedElementState(mSharedElements, originalImageViewState);
    if (mResultReceiver != null) {
        // We can't trust that the view will disappear on the same frame that the shared
        // element appears here. Assure that we get at least 2 frames for double-buffering.
        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.
                }
            }
        });
    }
}

這里要特別說(shuō)明的是

  • 這里沒(méi)有執(zhí)行ActivityA的非共享元素的進(jìn)場(chǎng)動(dòng)畫,因?yàn)樵谥耙呀?jīng)優(yōu)先調(diào)用了非共享元素的進(jìn)場(chǎng)動(dòng)畫
  • 雖然這里調(diào)用了ActivityA的共享元素動(dòng)畫,但是基本上并不會(huì)創(chuàng)建動(dòng)畫對(duì)象去執(zhí)行,因?yàn)?code>ActivityB傳過(guò)來(lái)的狀態(tài) 跟 ActivityA當(dāng)前的狀態(tài)是一模一樣的,除非你在某種情況下并在執(zhí)行動(dòng)畫之前 強(qiáng)制改變 ActivityA的當(dāng)前狀態(tài);所以你所看到的共享元素的退場(chǎng)動(dòng)畫其實(shí)是ActivityB的共享元素退場(chǎng)動(dòng)畫,而不是ActivityA

最后ActivityA的共享元素動(dòng)畫結(jié)束之后 會(huì)就調(diào)用onTransitionsComplete(不需要執(zhí)行動(dòng)畫,就會(huì)立馬觸發(fā)),將ActivityA的共享元素view從從decorView的ViewGroupOverlay中remove掉

到這里由ActivityB返回ActivityA的退場(chǎng)動(dòng)畫到這里基本上就結(jié)束了,至于最后的cancel等狀態(tài)清理就不介紹了

到這里我也用非常簡(jiǎn)單點(diǎn)的大白話總結(jié)一下ActivityB返回ActivityA的退場(chǎng)動(dòng)畫:

  • ActivityB的window背景設(shè)置成透明, 并執(zhí)行非共享元素的退場(chǎng)動(dòng)畫
  • 返回到ActivityA時(shí),將會(huì)執(zhí)行到performStart方法,并執(zhí)行非共享元素的進(jìn)場(chǎng)動(dòng)畫
  • ActivityB接收到ActivityA傳過(guò)來(lái)的共享元素狀態(tài),開(kāi)始執(zhí)行共享元素的退場(chǎng)動(dòng)畫
  • ActivityA接收到ActivityB的共享元素狀態(tài),繼續(xù)執(zhí)行共享元素動(dòng)畫(但由于兩個(gè)狀態(tài)沒(méi)有變化,所以并不會(huì)執(zhí)行動(dòng)畫,會(huì)立馬直接動(dòng)畫結(jié)束的回調(diào))

SharedElementCallback回調(diào)總結(jié)

最后我們?cè)诳偨Y(jié)以下SharedElementCallback回調(diào)的順序,因?yàn)槟阌锌赡軙?huì)自定義這個(gè)類 做一些特定的邏輯處理

當(dāng)是ActivityA打開(kāi)ActivityB時(shí)

ActivityA: ==Exit, onMapSharedElements
ActivityA: ==Exit, onCaptureSharedElementSnapshot
ActivityA: ==Exit, onCaptureSharedElementSnapshot
ActivityB: ==Enter, onMapSharedElements
ActivityA: ==Exit, onSharedElementsArrived
ActivityB: ==Enter, onSharedElementsArrived
ActivityB: ==Enter, onCreateSnapshotView
ActivityB: ==Enter, onRejectSharedElements
ActivityB: ==Enter, onCreateSnapshotView
ActivityB: ==Enter, onSharedElementStart
ActivityB: ==Enter, onSharedElementEnd

當(dāng)是ActivityB返回到ActivityA時(shí)

ActivityB: ==Enter, onMapSharedElements
ActivityA: ==Exit, onMapSharedElements
ActivityA: ==Exit, onCaptureSharedElementSnapshot
ActivityB: ==Enter, onCreateSnapshotView
ActivityB: ==Enter, onSharedElementEnd
ActivityB: ==Enter, onSharedElementStart
ActivityB: ==Enter, onSharedElementsArrived
ActivityA: ==Exit, onSharedElementsArrived
ActivityA: ==Exit, onRejectSharedElements
ActivityA: ==Exit, onCreateSnapshotView
ActivityA: ==Exit, onSharedElementStart
ActivityA: ==Exit, onSharedElementEnd

好了,到這里 我所要介紹的內(nèi)容已經(jīng)結(jié)束了,上面的源碼是針對(duì)Android30和Android31分析的(我在不同的時(shí)間段用不同的筆記本寫的,所以上面的源碼有的是Android30的源碼,有的是Android31的源碼)

最后再附上一張Activity共享元素動(dòng)畫的全程時(shí)序圖

點(diǎn)擊下載打開(kāi)

以上就是Android Activity共享元素動(dòng)畫示例解析的詳細(xì)內(nèi)容,更多關(guān)于Android Activity共享元素動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論