目前常用的沈浸式狀態欄的適配壹般都是,style中配置
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:windowTranslucentStatus">true</item>
又或者設置window的flag,如:
if (Build.VERSION.SDK_INT >= 21) {
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
window.setStatusBarColor(Color.TRANSPARENT);
}
然後配合 : fitsSystemWindows = true來解決部分控件不需要沈浸的問題
當然還有更野的,直接固定狀態欄高度,比如tim,網易雲音樂,手機淘寶等
當狀態欄高度為0時,瞬間就暴露出來了,當然也有做的好的,比如微信,等
在實際開發中這些api是完全無法滿足我們對視覺的追求,其中最常見的就是首頁,有的tab需要沈浸式,有些不需要,就完全無法完美實現.
然後就是虛擬導航欄了,設置
<item name="android:windowTranslucentNavigation">true</item> 後,布局會沈入導航欄,還有壹些其它的問題,解決起來也還不算太麻煩,這裏就不詳細說了
還有壹個就是虛擬鍵盤彈起時,布局被蓋住,或者彈起的問題了,當然這個也很好解決
只需要設置android:windowSoftInputMode="adjustResize" 或者 adjustPan就可以輕松解決絕大多數問題.
雖然上面的3個問題都可以或復雜,或簡單的解決,但是想要實現壹些高要求的需求時就有些無力了.
如果能夠監聽到狀態欄,導航欄,虛擬鍵盤高度的變化,是不是這些都可以迎刃而解,而且還能實現壹些更有意思的功能?
如何才能監聽到狀態欄,導航欄,虛擬鍵盤高度的變化呢?
接下來我們先研究研究系統是如何處理這些問題的.
首先我們先用布局查看器來查看界面的布局
可以看到,在未設置沈浸式狀態欄時,對第decorView的第0個view設置paddingtop和marginBottom,同時再在上面覆蓋兩個view來充當狀態欄和導航欄的顏色
當輸入法彈起時同樣是對第decorView的第0個view設置paddingBottom來實現的,這說明decorView的第0個view是可以監聽到我們想要的東西的.
通過查看系統源碼,終於讓我找到了vew的dispatchApplyWindowInsets這個方法
仔細查看了壹下這個源碼
終於讓我找到了給view設置padding的方法.
同時發現decorView重新了onApplyWindowInsets方法,並在這裏添加了充當狀態欄和導航欄顏色的背景view
decorView***有5處調用addView的邏輯,具體如何實現的就不深究了.
既然知道了系統的處理方案接下來我們只需在系統調用dispatchApplyWindowInsets時我們能收到相應的監聽就可以了.
還好系統為我們提供了setOnApplyWindowInsetsListener方法.
我們驗證下我們的成果
到這裏,要講的核心東西已經講完了,只剩下使用問題了.
至於如何使用可以根據個人情況,進行自行選擇.這裏只介紹兩種使用方式.
這裏先警告壹下,setOnApplyWindowInsetsListener後會破壞系統的事件傳遞機制
不過可以通過手動調用onApplyWindowInsets來進行解決
val viewGroup = window.decorView as ViewGroup
viewGroup.setOnApplyWindowInsetsListener { v, insets ->
println("--------$insets")
v.post {
//在這裏動態通知需要適配狀態欄,導航欄,輸入法彈起的控件
}
v.onApplyWindowInsets(insets)
}
2.完全中斷系統的適配方案,由我們自行處理,如:
val viewGroup = window.decorView as ViewGroup
viewGroup[0].setOnApplyWindowInsetsListener { v, insets ->
println("--------$insets")
v.post {
//在這裏動態通知需要適配狀態欄,導航欄,輸入法彈起的控件
insets.run {
//視情況而定
// v.setPadding(stableInsetLeft, stableInsetTop, stableInsetRight, stableInsetBottom)
v.setPadding(systemWindowInsetLeft, systemWindowInsetTop, systemWindowInsetRight, 0)
val h = findViewById<View>(R.id.llBottom)?.height ?: 0
findViewById<View>(R.id.flEt)?.setPadding(0,0,0,systemWindowInsetBottom-h)
}
}
insets.consumeSystemWindowInsets()
}
至於為什麽要在decorView 的第0個view進行監聽自行研究體會.
其中第二種方式可以在不需要對activity進行任何style或者flag的設置就可以實現沈浸式狀態欄效果.可玩性也更強.如果配上
<item name="android:windowTranslucentNavigation">true</item>那可玩性就更強了.至於能發揮到什麽程度就看各人的想法了.
至此,所有的東西都已經寫完了.
最後介紹壹個可以實現的場景:
在輸入法彈起時,我們可以自定義我們需要頂起的控件