在绘制View时,首先由根布局往下传WindowInsets
,SystemBar的尺寸在WindowInsets 中表示出来,比如Insets:(0, 63 , 0, 126), 表示StatusBar高度63,NavigationBar高度126。
根据默认的实现,如果设置了fitSystemWindows = true
,就会根据WindowInsets来设置padding,调用insets.consumeSystemWindowInsets()
表示这个WindowInsets被消耗了,之后就不会往下传。所以如果在布局文件了有多个View设置了fitSystemWindows = true
,那只有第一个起作用。
要自己根据WindowInsets来做处理,可复写onApplyWindowInsets
方法,也可以注册监听setOnApplyWindowInsetsListener
。
一个实现FitSystemWindows的自定义View:
package com.devbrackets.android.exomedia.ui.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.WindowInsets;
import android.widget.RelativeLayout;
/**
* A RelativeLayout that will abide by the fitsSystemWindows flag without
* consuming the event since Android has been designed to only allow
* one view with fitsSystemWindows=true at a time.
*/
public class FitsSystemWindowRelativeLayout extends RelativeLayout {
private Rect originalPadding;
public FitsSystemWindowRelativeLayout(Context context) {
super(context);
setup();
}
public FitsSystemWindowRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setup();
}
public FitsSystemWindowRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FitsSystemWindowRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setup();
}
/**
* Makes sure the padding is correct for the orientation
*/
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//If the system navigation bar can move, then clear out the previous insets before
// fitSystemWindows(...) or onApplyWindowInsets(...) is called
//This fixes the issue
if (navBarCanMove()) {
setup();
}
}
@Override
@SuppressWarnings("deprecation")
protected boolean fitSystemWindows(@NonNull Rect insets) {
updatePadding(insets);
return false;
}
@Override
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Rect windowInsets = new Rect(
insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom()
);
fitSystemWindows(windowInsets);
return insets;
}
/**
* Updates the views padding so that any children views are correctly shown next to, and
* below the system bars (NavigationBar and Status/SystemBar) instead of behind them.
*/
private void setup() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
setFitsSystemWindows(true);
}
if (originalPadding == null) {
originalPadding = new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
}
updatePadding(new Rect());
}
/**
* Updates the layouts padding by using the original padding and adding
* the values found in the insets.
*
* @param insets The Rect containing the additional insets to use for padding
*/
private void updatePadding(Rect insets) {
int rightPadding = originalPadding.right + insets.right;
int bottomPadding = originalPadding.bottom + insets.bottom;
int topPadding = originalPadding.top + insets.top;
setPadding(originalPadding.left, topPadding, rightPadding, bottomPadding);
}
/**
* Determines if the Navigation controller bar can move. This will typically only be
* true for phones.
*
* @return True if the system navigation buttons can move sides
*/
private boolean navBarCanMove() {
return this.getResources().getConfiguration().smallestScreenWidthDp <= 600;
}
}
这个自定义View在onApplyWindowInsets中根据上面传下来的WindowInsets做了相应的处理,但是并没有调用insets.consumeSystemWindowInsets()
,所以WindowInsets还是会继续往下传,这样可以解决同一个布局里面只有第一个fitSystemWindows = true
才起作用的问题。