- 原文地址:Windows Insets + Fragment Transitions: A tale of woe
- 原文做者:Chris Banes
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:LeeSniper
- 校對者:Starrier
這篇文章是我寫的關於 fragment 過渡動畫的小系列中的第二篇。第一篇能夠經過下面的連接查看,裏面寫了如何讓 fragment 過渡動畫開始工做。html
在我開始進一步探討以前,我會假設你知道什麼是 WindowsInsets 以及它們是如何分發的。若是你不知道,我建議你先看這個演講(是的,這是個人演講 🙋)前端
我須要坦白。當我在寫本系列第一篇博客文章的時候,我對視頻作了點手腳。實際上我遇到了 WindowInsets 的問題,也就是說我實際上最終獲得的是如下結果:java
過渡動畫破壞了狀態欄的效果。android
Woops,跟我在第一篇文章中展現的效果不太同樣 🤐。我不想讓第一篇文章變得太複雜,因此決定單獨寫這篇文章。不管如何,你能夠看到當添加過渡動畫以後,咱們忽然失去了全部狀態欄的效果,並且視圖被推到狀態欄的下面。ios
這兩個 fragment 爲了在系統欄下面進行繪製都大量使用了 WindowInsets。Fragment A 使用了 CoordinatorLayout 和 AppBarLayout,而 Fragment B 使用自定義 WindowInsets 來處理(經過一個 OnApplyWindowInsetsListener)。不管它們是如何實現的,過渡動畫都會混淆二者。git
那麼爲何會這樣呢?其實當你在使用 fragment 過渡動畫時,退出(Fragment A)和進入(Fragment B)的內容視圖實際上經歷瞭如下幾個過程:github
這一切聽起來都很好,那爲何會忽然影響到 WindowInsets 的效果呢?這是由於在過渡的過程當中,兩個 fragment 的視圖都存在於容器中。windows
可是這聽起來徹底 OK 啊,不是嗎?然而在個人場景中,這兩個 fragment 的視圖都想要處理和消費 WindowInsets,由於它們都指望在屏幕上顯示惟一的「主」視圖。但是隻有其中的一個視圖會收到 WindowInsets:也就是第一個子 view。這取決於 ViewGroup 是如何分發 WindowInsets 的,也就是經過按順序遍歷它的子節點直到其中的一個消費了 WindowInsets。 若是第一個子 view(就是這裏的 Fragment A)消費了 WindowInsets,任何後續的子 view(就是這裏的 Fragment B)都不會獲得它們,咱們最終就會獲得這種狀況。後端
讓咱們再來一步一步檢查一遍,只是這一次加上分發 windowinsets 的時機:app
這個修復實際上相對簡單:咱們只須要確保兩個視圖都可以拿到 WindowInsets。
我實現這一點的方法是經過在容器視圖(在這個例子中就是在宿主 activity)裏添加一個 OnApplyWindowInsetsListener,它會手動分發 WindowInsets 給全部的子 view,直到其中一個子 view 消費掉這個 WindowInsets。
fragment_container.setOnApplyWindowInsetsListener { view, insets ->
var consumed = false
(view as ViewGroup).forEach { child ->
// Dispatch the insets to the child
val childResult = child.dispatchApplyWindowInsets(insets)
// If the child consumed the insets, record it
if (childResult.isConsumed) {
consumed = true
}
}
// If any of the children consumed the insets, return
// an appropriate value
if (consumed) insets.consumeSystemWindowInsets() else insets
}
複製代碼
在咱們應用這個修復以後,這兩個 fragment 都會收到 WindowInsets,而後咱們就會獲得第一篇文章中實際顯示的結果:
還有一件我差點忘了寫的小事。若是你要在 fragment 裏面處理 WindowInsets,不管是隱式(經過使用 AppBarLayout 等)仍是顯式,你須要確保請求了一些 WindowInsets。只須要調經過 requestApplyInsets() 就能很容易作到:
override fun onViewCreated(view: View, icicle: Bundle) {
super.onViewCreated(view, savedInstanceState)
// yadda, yadda
ViewCompat.requestApplyInsets(view)
}
複製代碼
你必須這樣作是由於窗口只有在整個視圖層級整體的系統 UI 可見性的值發生改變的時候纔會自動分發 WindowInsets。 因爲有時你的兩個 fragment 可能提供徹底相同的值,整體的值不會改變,所以系統將忽略這個「改變」。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。