深入理解 Shared Element Transition (part 3a)
- 原文鏈接 : Shared Element Transitions In-Depth (part 3a)
- 作者 : Alex Lockwood
- 譯文出自 : 開(kāi)發(fā)技術(shù)前線 www.devtf.cn
- 譯者 : tiiime
- 校對(duì)者: chaossss
- 狀態(tài) : 完成
深入理解共享元素 Transition
這篇文章會(huì)深度分析共享元素 transitions 和它在 Activity & Fragment Transitions API 中的作用。這篇文章是下面這個(gè)系列中的第三篇:
- Part 1: 在 Activity 和 Fragment 中使用 Transition
- Part 2: 深入理解 Content Transition
- Part 3a: 深入理解共享元素 Transition
- Part 3b: 延遲共享元素的 Transition
- Part 3c: 共享元素回調(diào)實(shí)踐 (coming soon!)
- Part 4: Activity & Fragment 過(guò)渡動(dòng)畫(huà)示例(coming soon!)
Part 3 會(huì)分成三個(gè)部分: part3a 介紹 共享元素 transitions 的底層操作,part3b 和 part3c
主要關(guān)注 API 的具體實(shí)現(xiàn)細(xì)節(jié),例如推遲某些 共享元素 transition 的重要性和如何實(shí)現(xiàn)
SharedElementCallbacks。
我們首先會(huì)總結(jié)下在 part 1 中提到的關(guān)于 共享元素 transition 的知識(shí)點(diǎn),然后說(shuō)一說(shuō)在 Android Lollipop 中是怎樣使用它來(lái)構(gòu)建合適的過(guò)渡動(dòng)畫(huà)。
什么是 共享元素 Transition ?
共享元素 transition 決定了 共享元素 視圖(也叫做主角視圖)在
Activity/Fragment 場(chǎng)景過(guò)渡時(shí)的動(dòng)畫(huà)效果。共享元素 的動(dòng)畫(huà)
在被調(diào)用 Activity/Fragment 的 進(jìn)入/返回 共享元素 transition
<a id="1" href="#b1">(1)</a> 中執(zhí)行,
可以通過(guò)下面的Window/Fragment 方法設(shè)置:
-
setSharedElementEnterTransition() - B 的 進(jìn)入 共享元素 transition ,執(zhí)行將
共享元素視圖 從 A 中的起始位置移動(dòng)到它在 B 中的最終位置的動(dòng)畫(huà)。 -
setSharedElementReturnTransition() - B 的 返回 共享元素 transition ,執(zhí)行將
共享元素視圖 從 B 中起始位置移動(dòng)到它在 A 中的最終位置的動(dòng)畫(huà)。
Video 3.1 展示了在 Google Play Music 中是怎樣使用共享元素 transition
的。這個(gè) transition 包含兩個(gè)元素:一個(gè) ImageView和它的父視圖 CardView。
Transition 期間,CardView 會(huì)擴(kuò)展到全屏或收縮回原狀,
ImageView 能在這兩個(gè) Activity 里無(wú)縫的銜接。
在 part1 里只是簡(jiǎn)單的介紹了下這個(gè)話題,這篇文章將會(huì)對(duì) 共享元素 transition
做更深度的分析。例如 共享元素 Transition 在底層是如何實(shí)現(xiàn)的?都有哪些類型的 Transition 對(duì)象可以使用? Transition 期間 共享元素視圖 是在哪里怎樣繪制的?接下來(lái)的幾章里
我們會(huì)逐個(gè)解答這些問(wèn)題。
深入共享元素 Transitions 底層
之前的文章已經(jīng)介紹過(guò) Transition 的兩個(gè)主要任務(wù)分別是獲取目標(biāo)視圖的 開(kāi)始&結(jié)束 狀態(tài)和創(chuàng)建這兩個(gè)狀態(tài)間視圖的過(guò)渡動(dòng)畫(huà)(Animator)。共享元素 Transition 也一樣:
在創(chuàng)建動(dòng)畫(huà)前需要捕獲每一個(gè)
共享元素視圖的起始和結(jié)束狀態(tài)(就是共享元素在 調(diào)用/被調(diào)用 Activity/Fragment
里的位置,大小和外觀)。有了這些信息 共享元素 Transition 就可以確定每一個(gè) 共享元素
視圖應(yīng)該執(zhí)行的動(dòng)畫(huà)。
和 Content Transitions 的底層相似,框架通過(guò)在運(yùn)行時(shí)明確的
更改每個(gè) 共享元素視圖 的屬性將這個(gè)狀態(tài)信息提供給 共享元素 Transition 。
更準(zhǔn)確地說(shuō) Activity A 啟動(dòng) Activity B 時(shí)將會(huì)出現(xiàn)以下事件:<a id="2" href="#b2">(2)</a>
- Activity A 調(diào)用 startActivity() 構(gòu)造,測(cè)量,布局了一個(gè)
最初背景色為透明的半透明窗口 Activity B 。 - 框架將 B 中每一個(gè)共享元素視圖復(fù)位到對(duì)應(yīng)的原來(lái)在 A 中時(shí)的位置,接著 B 的進(jìn)入 transition 捕獲 B 中所有共享元素視圖的起始狀態(tài)。
- 框架將 B 中每一個(gè)共享元素視圖復(fù)位到對(duì)應(yīng)的在 B 的最終位置,接著 B 的進(jìn)入 transition 捕獲 B 中所有共享元素視圖的結(jié)束狀態(tài)。
-
B 的進(jìn)入 transition 比較所有共享元素視圖的起始和結(jié)束狀態(tài),根據(jù)它們的不同
創(chuàng)建一個(gè) Animator。 - 框架命令 A 隱藏共享元素視圖,并運(yùn)行返回的 Animator。B 中的
共享元素視圖到位之后,B 的窗口背景在 **A **上逐漸顯示,直到 B
完全的顯示出來(lái),transition 運(yùn)行完畢。
Content transitions 是根據(jù)每個(gè)過(guò)渡視圖的可見(jiàn)性變化來(lái)調(diào)節(jié)的,共享元素 transition
是根據(jù)每個(gè)共享元素視圖的位置,大小和外觀的變化來(lái)調(diào)節(jié)的。從 API 21 開(kāi)始,框架提供了
幾個(gè)自定義共享元素場(chǎng)景切換動(dòng)畫(huà)的 Transition 實(shí)現(xiàn)。
-
ChangeBounds - 捕獲共享元素布局邊界根據(jù)不同構(gòu)造動(dòng)畫(huà)。
ChangeBounds 在共享元素 Transition 中經(jīng)常使用,大多數(shù)共享元素在兩個(gè)
Activity/Fragment 間會(huì)有大小 或/和 位置不同。 -
ChangeTransform - 捕獲共享元素縮放和角度,
根據(jù)不同構(gòu)建動(dòng)畫(huà)<a id="3" href="#b3">(3)</a>。 -
ChangeClipBounds - 捕獲共享元素的 clip bounds
(剪輯邊界) ,根據(jù)不同構(gòu)建動(dòng)畫(huà)。 -
ChangeImageTransform - 捕獲共享元素 ImageView 的
變換矩陣( transform matrices) ,根據(jù)不同構(gòu)建動(dòng)畫(huà)。結(jié)合 ChangeBounds,
可以讓 ImageView
無(wú)縫的改變大小,形狀和 ImageView.ScaleType 。 -
@android:transition/move - 一個(gè) TransitionSet ,同時(shí)執(zhí)行上面四種
transition 。在 part 1 里提到過(guò),如果沒(méi)有明確的聲明 進(jìn)入/返回 共享元素
transition ,框架會(huì)默認(rèn)運(yùn)行這個(gè) transition。
在上面的例子中,我們還可以發(fā)現(xiàn) 共享元素視圖實(shí)例并沒(méi)有在 Activities/Fragments 間
“共享”。事實(shí)上,進(jìn)入/返回 共享元素 transitions期間,用戶看到的絕大多數(shù)東西都是在
B 的 content view 中繪制的??蚣懿](méi)有從 A 向 B
傳遞 共享元素視圖實(shí)例,
而是采用了不同的方法實(shí)現(xiàn)相同的視覺(jué)效果。當(dāng) A 啟動(dòng) B ,框架收集 A
中共享元素的所有相關(guān)信息,并傳遞給 B。接下來(lái) B 使用這些信息初始化
共享元素視圖的起始狀態(tài)(它們?cè)?A 中時(shí)對(duì)應(yīng)的大小,位置和外觀)。Transition
開(kāi)始時(shí),B 中除了共享元素視圖外的所有東西都被初始化為對(duì)用戶不可見(jiàn)。Tansition
的執(zhí)行過(guò)程中,框架將 B 的 Activity 窗口逐漸顯示,直到 B
中共享元素結(jié)束動(dòng)畫(huà)窗口變?yōu)椴煌该鳌?/p>
使用共享元素 Overlay <a id="4" href="#b4">(4)</a>
最后,如果想要完全理解共享元素 transition 的運(yùn)作,我們必須先說(shuō)說(shuō)共享元素 overlay。
可能不是很明顯,共享元素默認(rèn)是在整個(gè)窗口視圖層的頂層 ViewOverlay
上繪制。簡(jiǎn)單介紹下 ,
ViewOverlay 這個(gè)類是在 API 18 中為了方便在視圖層頂層
繪制引入的。添加到視圖 ViewOverlay 之中的Drawable
和 view (甚至是一個(gè) ViewGroup 的子類) ,
將會(huì)被繪制在視圖的最上層。這就解釋了框架為什么
默認(rèn)選擇在窗口視圖層的 ViewOverlay 中繪制共享元素。
共享元素視圖應(yīng)該是貫穿整個(gè) transition 的焦點(diǎn);
如果 transitioning views 意外的繪制在共享元素之上就會(huì)
破壞這個(gè)效果<a id="5" href="#b5">(5)</a>。
雖然共享元素默認(rèn)繪制在共享元素的 ViewOverlay 之中,但是
框架也提供了關(guān)閉 overlay 的方法,只要調(diào)用
Window#setSharedElementsUseOverlay(false)
就可以了。如果你關(guān)閉了 overlay
,要留意這樣做可能會(huì)引起的副作用。例如,Video 3.2
執(zhí)行了一個(gè)簡(jiǎn)單的共享元素 transition 兩次,
一次開(kāi)啟和一次關(guān)閉 共享元素 overlay 。第一次達(dá)到了預(yù)期想要的結(jié)果,
第二次關(guān)閉 overlay 后運(yùn)行的效果不理想。Transition view 從底部向上移入
調(diào)用 Activity 的 content view 時(shí)擋住了部分 共享元素 ImageView 。雖然
可以改變?cè)?View 上繪制視圖的順序或者通過(guò)在共享元素 parent 里調(diào)用
setClipChildren(false) 這些旁門(mén)左道來(lái)修復(fù)問(wèn)題,但是與可能帶來(lái)的維護(hù)問(wèn)題
相比真是得不償失??傊?,除非你感覺(jué)必須要關(guān)掉共享元素 overlay 才能達(dá)到你想要的效果,
其他情況盡量不要關(guān)閉它,這樣會(huì)保持代碼簡(jiǎn)潔,并且共享元素 transition 效果更引人注目。
結(jié)語(yǔ)
綜上所訴,這篇文章講了三個(gè)重點(diǎn):
- 共享元素 transition 確定 共享元素視圖(主角視圖) 從一個(gè) Activity/Fragment 移動(dòng)到
另一個(gè)其間場(chǎng)景過(guò)渡的動(dòng)畫(huà)。 - 共享元素 transition 是根據(jù)每一個(gè) 共享元素視圖 的位置,大小,和外觀的變化調(diào)節(jié)的。
- 共享元素默認(rèn)是繪制在窗口視圖的頂層 ViewOverlay 上面的。
希望這篇文章對(duì)你有所幫助 ~
1 Note that the Activity Transition API gives you the ability to also specify exit and reenter shared element transitions using the setSharedElementExitTransition() and setSharedElementReenterTransition() methods, although doing so is usually not necessary. For an example illustrating one possible use case, check out this blog post. For an explanation why exit and reenter shared element transitions are not available for Fragment Transitions, see George Mount's answer and comments in this StackOverflow post. ?
2 A similar sequence of events occurs during the exit/return/reenter transitions for both Activities and Fragments. ?
3 One other subtle feature of ChangeTransform is that it can detect and handle changes made to a shared element view's parent during a transition. This comes in handy when, for example, the shared element's parent has an opaque background and is by default selected to be a transitioning view during the scene change. In this case, the ChangeTransform will detect that the shared element's `parent is being actively modified by the content transition, pull out the shared element from its parent, and animate the shared element separately. See George Mount's StackOverflow answer for more information. ?
4 Note that this section only pertains to Activity Transitions. Unlike Activity Transitions, shared elements are not drawn in a ViewOverlay by default during Fragment Transitions. That said, you can achieve a similar effect by applying a ChangeTransform transition, which will have the shared element drawn on top of the hierarchy in a ViewOverlay if it detects that its parent has changed. See this StackOverflow post for more information. ?
5 Note that one negative side-effect of having shared elements drawn on top of the entire view hierarchy is that this means it will become possible for shared elements to draw on top of the System UI (such as the status bar, navigation bar, and action bar). For more information on how you can prevent this from happening, see this Google+ post. ?
Activity Transition API 也提供了
setSharedElementExitTransition() 和 setSharedElementReenterTransition()
這兩個(gè)方法來(lái)設(shè)置 退出/重入 共享元素過(guò)渡,雖然通常來(lái)說(shuō)是不必要的。
這篇文章介紹了一個(gè)可能會(huì)遇到的用例。在這個(gè) stackoverflow
提問(wèn)中 George Mount 的回答解釋了為什么 退出/重入 共享元素 transition 在
Fragment Transitions 中不可用。 <a id="b1" href="#1">?</a>Activities 和 Fragments 的 退出/返回/重入 transition 過(guò)程中出現(xiàn)事件序列相似 <a id="b2" href="#2">?</a>
ChangeTransform 還有一個(gè)超贊的特性,它可以檢測(cè)并處理共享元素父視圖過(guò)渡期間
的改變。當(dāng)共享元素父視圖有一個(gè)不透明背景,在場(chǎng)景變換過(guò)程中默認(rèn)被選為
transitioning view 時(shí),ChangeTransform 就有了用武之地。如果它檢測(cè)出
共享元素父視圖已被 content transition 更改,就會(huì)將共享元素提取出來(lái),單獨(dú)執(zhí)行
共享元素的動(dòng)畫(huà)。StackOverflow answer 這里有 George Mount
的詳細(xì)說(shuō)明。
<a id="b3" href="#3">?</a>注意,這部分只與 Activity Transition 有關(guān)。和Activity Transition 不同,F(xiàn)ragment Transition
期間共享元素默認(rèn)不在 ViewOverlay 中繪制。盡管如此,你仍可以使用 ChangeTransform
transition 來(lái)達(dá)到相似的效果,如果它檢測(cè)到父視圖改變了,就會(huì)把共享元素繪制在
ViewOverlay 的頂層。StackOverflow 這里有更多信息。
<a id="b4" href="#4">?</a>注意,將共享元素繪制在整個(gè)圖層最頂層也有一些負(fù)面效果。有可能
會(huì)將共享元素繪制在 System UI 之上(比如 status bar, navigation bar還有 action bar)。
解決方法看這里 Google+ post。
<a id="b5" href="#5">?</a>