深入理解 Shared Element Transition (part 3a)
- 原文鏈接 : Shared Element Transitions In-Depth (part 3a)
- 作者 : Alex Lockwood
- 譯文出自 : 開發(fā)技術前線 www.devtf.cn
- 譯者 : tiiime
- 校對者: chaossss
- 狀態(tài) : 完成
深入理解共享元素 Transition
這篇文章會深度分析共享元素 transitions 和它在 Activity & Fragment Transitions API 中的作用。這篇文章是下面這個系列中的第三篇:
- Part 1: 在 Activity 和 Fragment 中使用 Transition
- Part 2: 深入理解 Content Transition
- Part 3a: 深入理解共享元素 Transition
- Part 3b: 延遲共享元素的 Transition
- Part 3c: 共享元素回調(diào)實踐 (coming soon!)
- Part 4: Activity & Fragment 過渡動畫示例(coming soon!)
Part 3 會分成三個部分: part3a 介紹 共享元素 transitions 的底層操作程剥,part3b 和 part3c
主要關注 API 的具體實現(xiàn)細節(jié)博其,例如推遲某些 共享元素 transition 的重要性和如何實現(xiàn)
SharedElementCallbacks。
我們首先會總結(jié)下在 part 1 中提到的關于 共享元素 transition 的知識點,然后說一說在 Android Lollipop 中是怎樣使用它來構(gòu)建合適的過渡動畫。
什么是 共享元素 Transition ?
共享元素 transition 決定了 共享元素 視圖(也叫做主角視圖)在
Activity/Fragment 場景過渡時的動畫效果。共享元素 的動畫
在被調(diào)用 Activity/Fragment 的 進入/返回 共享元素 transition
<a id="1" href="#b1">(1)</a> 中執(zhí)行肚邢,
可以通過下面的Window/Fragment 方法設置:
-
setSharedElementEnterTransition() - B 的 進入 共享元素 transition ,執(zhí)行將
共享元素視圖 從 A 中的起始位置移動到它在 B 中的最終位置的動畫拭卿。 -
setSharedElementReturnTransition() - B 的 返回 共享元素 transition 骡湖,執(zhí)行將
共享元素視圖 從 B 中起始位置移動到它在 A 中的最終位置的動畫。
Video 3.1 展示了在 Google Play Music 中是怎樣使用共享元素 transition
的峻厚。這個 transition 包含兩個元素:一個 ImageView和它的父視圖 CardView响蕴。
Transition 期間,CardView 會擴展到全屏或收縮回原狀目木,
ImageView 能在這兩個 Activity 里無縫的銜接换途。
在 part1 里只是簡單的介紹了下這個話題懊渡,這篇文章將會對 共享元素 transition
做更深度的分析刽射。例如 共享元素 Transition 在底層是如何實現(xiàn)的?都有哪些類型的 Transition 對象可以使用? Transition 期間 共享元素視圖 是在哪里怎樣繪制的剃执?接下來的幾章里
我們會逐個解答這些問題誓禁。
深入共享元素 Transitions 底層
之前的文章已經(jīng)介紹過 Transition 的兩個主要任務分別是獲取目標視圖的 開始&結(jié)束 狀態(tài)和創(chuàng)建這兩個狀態(tài)間視圖的過渡動畫(Animator)。共享元素 Transition 也一樣:
在創(chuàng)建動畫前需要捕獲每一個
共享元素視圖的起始和結(jié)束狀態(tài)(就是共享元素在 調(diào)用/被調(diào)用 Activity/Fragment
里的位置肾档,大小和外觀)摹恰。有了這些信息 共享元素 Transition 就可以確定每一個 共享元素
視圖應該執(zhí)行的動畫。
和 Content Transitions 的底層相似怒见,框架通過在運行時明確的
更改每個 共享元素視圖 的屬性將這個狀態(tài)信息提供給 共享元素 Transition 俗慈。
更準確地說 Activity A 啟動 Activity B 時將會出現(xiàn)以下事件:<a id="2" href="#b2">(2)</a>
- Activity A 調(diào)用 startActivity() 構(gòu)造,測量遣耍,布局了一個
最初背景色為透明的半透明窗口 Activity B 闺阱。 - 框架將 B 中每一個共享元素視圖復位到對應的原來在 A 中時的位置,接著 B 的進入 transition 捕獲 B 中所有共享元素視圖的起始狀態(tài)舵变。
- 框架將 B 中每一個共享元素視圖復位到對應的在 B 的最終位置酣溃,接著 B 的進入 transition 捕獲 B 中所有共享元素視圖的結(jié)束狀態(tài)瘦穆。
-
B 的進入 transition 比較所有共享元素視圖的起始和結(jié)束狀態(tài),根據(jù)它們的不同
創(chuàng)建一個 Animator赊豌。 - 框架命令 A 隱藏共享元素視圖扛或,并運行返回的 Animator。B 中的
共享元素視圖到位之后碘饼,B 的窗口背景在 **A **上逐漸顯示熙兔,直到 B
完全的顯示出來,transition 運行完畢艾恼。
Content transitions 是根據(jù)每個過渡視圖的可見性變化來調(diào)節(jié)的黔姜,共享元素 transition
是根據(jù)每個共享元素視圖的位置,大小和外觀的變化來調(diào)節(jié)的蒂萎。從 API 21 開始秆吵,框架提供了
幾個自定義共享元素場景切換動畫的 Transition 實現(xiàn)。
-
ChangeBounds - 捕獲共享元素布局邊界根據(jù)不同構(gòu)造動畫五慈。
ChangeBounds 在共享元素 Transition 中經(jīng)常使用纳寂,大多數(shù)共享元素在兩個
Activity/Fragment 間會有大小 或/和 位置不同。 -
ChangeTransform - 捕獲共享元素縮放和角度泻拦,
根據(jù)不同構(gòu)建動畫<a id="3" href="#b3">(3)</a>毙芜。 -
ChangeClipBounds - 捕獲共享元素的 clip bounds
(剪輯邊界) ,根據(jù)不同構(gòu)建動畫争拐。 -
ChangeImageTransform - 捕獲共享元素 ImageView 的
變換矩陣( transform matrices) 腋粥,根據(jù)不同構(gòu)建動畫。結(jié)合 ChangeBounds架曹,
可以讓 ImageView
無縫的改變大小隘冲,形狀和 ImageView.ScaleType 。 -
@android:transition/move - 一個 TransitionSet 绑雄,同時執(zhí)行上面四種
transition 展辞。在 part 1 里提到過,如果沒有明確的聲明 進入/返回 共享元素
transition 万牺,框架會默認運行這個 transition罗珍。
在上面的例子中,我們還可以發(fā)現(xiàn) 共享元素視圖實例并沒有在 Activities/Fragments 間
“共享”脚粟。事實上覆旱,進入/返回 共享元素 transitions期間,用戶看到的絕大多數(shù)東西都是在
B 的 content view 中繪制的核无】鄢框架并沒有從 A 向 B
傳遞 共享元素視圖實例,
而是采用了不同的方法實現(xiàn)相同的視覺效果。當 A 啟動 B 画舌,框架收集 A
中共享元素的所有相關信息堕担,并傳遞給 B。接下來 B 使用這些信息初始化
共享元素視圖的起始狀態(tài)(它們在 A 中時對應的大小曲聂,位置和外觀)霹购。Transition
開始時,B 中除了共享元素視圖外的所有東西都被初始化為對用戶不可見朋腋。Tansition
的執(zhí)行過程中齐疙,框架將 B 的 Activity 窗口逐漸顯示,直到 B
中共享元素結(jié)束動畫窗口變?yōu)椴煌该鳌?/p>
使用共享元素 Overlay <a id="4" href="#b4">(4)</a>
最后旭咽,如果想要完全理解共享元素 transition 的運作贞奋,我們必須先說說共享元素 overlay。
可能不是很明顯穷绵,共享元素默認是在整個窗口視圖層的頂層 ViewOverlay
上繪制轿塔。簡單介紹下 ,
ViewOverlay 這個類是在 API 18 中為了方便在視圖層頂層
繪制引入的仲墨。添加到視圖 ViewOverlay 之中的Drawable
和 view (甚至是一個 ViewGroup 的子類) 勾缭,
將會被繪制在視圖的最上層。這就解釋了框架為什么
默認選擇在窗口視圖層的 ViewOverlay 中繪制共享元素目养。
共享元素視圖應該是貫穿整個 transition 的焦點俩由;
如果 transitioning views 意外的繪制在共享元素之上就會
破壞這個效果<a id="5" href="#b5">(5)</a>。
雖然共享元素默認繪制在共享元素的 ViewOverlay 之中癌蚁,但是
框架也提供了關閉 overlay 的方法幻梯,只要調(diào)用
Window#setSharedElementsUseOverlay(false)
就可以了。如果你關閉了 overlay
努释,要留意這樣做可能會引起的副作用碘梢。例如,Video 3.2
執(zhí)行了一個簡單的共享元素 transition 兩次洽洁,
一次開啟和一次關閉 共享元素 overlay 痘系。第一次達到了預期想要的結(jié)果菲嘴,
第二次關閉 overlay 后運行的效果不理想饿自。Transition view 從底部向上移入
調(diào)用 Activity 的 content view 時擋住了部分 共享元素 ImageView 。雖然
可以改變在 View 上繪制視圖的順序或者通過在共享元素 parent 里調(diào)用
setClipChildren(false) 這些旁門左道來修復問題龄坪,但是與可能帶來的維護問題
相比真是得不償失昭雌。總之健田,除非你感覺必須要關掉共享元素 overlay 才能達到你想要的效果烛卧,
其他情況盡量不要關閉它,這樣會保持代碼簡潔,并且共享元素 transition 效果更引人注目总放。
結(jié)語
綜上所訴呈宇,這篇文章講了三個重點:
- 共享元素 transition 確定 共享元素視圖(主角視圖) 從一個 Activity/Fragment 移動到
另一個其間場景過渡的動畫。 - 共享元素 transition 是根據(jù)每一個 共享元素視圖 的位置局雄,大小甥啄,和外觀的變化調(diào)節(jié)的。
- 共享元素默認是繪制在窗口視圖的頂層 ViewOverlay 上面的炬搭。
希望這篇文章對你有所幫助 ~
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()
這兩個方法來設置 退出/重入 共享元素過渡蜈漓,雖然通常來說是不必要的。
這篇文章介紹了一個可能會遇到的用例宫盔。在這個 stackoverflow
提問中 George Mount 的回答解釋了為什么 退出/重入 共享元素 transition 在
Fragment Transitions 中不可用融虽。 <a id="b1" href="#1">?</a>Activities 和 Fragments 的 退出/返回/重入 transition 過程中出現(xiàn)事件序列相似 <a id="b2" href="#2">?</a>
ChangeTransform 還有一個超贊的特性,它可以檢測并處理共享元素父視圖過渡期間
的改變灼芭。當共享元素父視圖有一個不透明背景有额,在場景變換過程中默認被選為
transitioning view 時,ChangeTransform 就有了用武之地彼绷。如果它檢測出
共享元素父視圖已被 content transition 更改谆吴,就會將共享元素提取出來,單獨執(zhí)行
共享元素的動畫苛预。StackOverflow answer 這里有 George Mount
的詳細說明句狼。
<a id="b3" href="#3">?</a>注意,這部分只與 Activity Transition 有關热某。和Activity Transition 不同腻菇,F(xiàn)ragment Transition
期間共享元素默認不在 ViewOverlay 中繪制。盡管如此昔馋,你仍可以使用 ChangeTransform
transition 來達到相似的效果筹吐,如果它檢測到父視圖改變了,就會把共享元素繪制在
ViewOverlay 的頂層秘遏。StackOverflow 這里有更多信息丘薛。
<a id="b4" href="#4">?</a>注意,將共享元素繪制在整個圖層最頂層也有一些負面效果邦危。有可能
會將共享元素繪制在 System UI 之上(比如 status bar, navigation bar還有 action bar)洋侨。
解決方法看這里 Google+ post。
<a id="b5" href="#5">?</a>