官方描述:bash
根據系統窗體裏的元素好比狀態欄來調整View的佈局。若是被設爲true,控件的padding將會被調整爲頂部留出一個statusBar的空間。相似於僞代碼paddingTop="statusBarHeight"。ide
重點說明:佈局
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
,默認不會有這個flag,佈局不會延伸到狀態欄下),該屬性纔會起做用上述二、3點和官方描述的行爲都是默認行爲,而這個行爲能夠經過自定義View來進行個性化,好比CoordinateLayout就重載了這種行爲(能夠參考下方連接文章)ui
最近一個項目中,有幾個界面是一個Activity裝載多個Fragment的形式,爲了實現沉浸式佈局,將Activity的decorView加上View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
(使佈局延伸到狀態欄),全部Fragment的佈局中,則將在最頂部的View(好比Toolbar)的fitSystemWindows設爲true。 卻驚訝地發現,只有第一個被加入到Activity的Fragment顯示正常,隨後被添加的Fragment都適配錯誤,以下圖:this
緣由spa
添加Fragment的過程可看做往容器佈局添加子View的過程。當第一個Fragment被添加到容器佈局時,容器佈局找出fitSystemWindows爲true的子View,併爲其paddingTop一個狀態欄的高度,當其餘Fragment隨後被添加時,上述的paddingTop適配已經被消費過一次,並不會再爲其後添加的View進行適配(默認行爲),所以咱們要自定義容器佈局View,使其每一個子View都消費一次ViewGroup分發的WindowsInsets,至關於每一個子Fragment都能適配狀態欄.net
注意code
此方法實現的佈局容器會對其每一個子View都適配一次cdn
實現代碼blog
我通常用FrameLayout做爲容器佈局,所以繼承了FrameLayout,每次addView都requestApplyInsets請求分發WindowInsets,而且保存當前添加的子View,在重載方法onApplyWindowInsets中調用子View的dispatchApplyWindowInsets,使每一個子View都有機會消費一次insets
class WindowInsetsFrameLayout: FrameLayout {
private var requestView: View? = null
constructor(context: Context): this(context, null)
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
setOnHierarchyChangeListener(object : OnHierarchyChangeListener {
override fun onChildViewAdded(parent: View?, child: View?) {
requestView = child
requestApplyInsets() //子View添加時申請解析inset
}
override fun onChildViewRemoved(parent: View?, child: View?) {}
})
}
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets? {
val t = requestView
return if (t == null) {
super.onApplyWindowInsets(insets)
} else {
val res = t.dispatchApplyWindowInsets(insets) //子View解析
requestView = null
res
}
}
}
複製代碼
參考連接