fitSystemWindow的作用
在指定的view上添加padding,来空出系统的状态栏和导航栏的空间。通常配合透明状态栏使用。
失效的原因
在一个Activity的View树中,fitSystemWindow的传递会被上层拦截。
WindowInsets
windowInsets保存了窗口信息。包括系统控件占用的窗口空间等。状态栏的高度就是通过这个对象传递的。
自下而上传递WindowInsets
在ViewGroup中传递WindowInsets的方法
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
insets = super.dispatchApplyWindowInsets(insets);
if (!insets.isConsumed()) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
insets = getChildAt(i).dispatchApplyWindowInsets(insets);
if (insets.isConsumed()) {
break;
}
}
}
return insets;
}
当有一个chid消费了windowInsets,那就停止传递。
失效问题
从上面一节可以看出,失效的真正原因是WindowInsets被上层view消费了。所以没有传递下来。
解决方法 api>=20
自定义上层ViewGroup。以FrameLayout为例
public class WindowInsertFrameLayout extends FrameLayout {
public WindowInsertFrameLayout(Context context) {
this(context,null);
}
public WindowInsertFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
//当有child添加时,请求再次分发一次WindowInsets
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
requestApplyInsets();
}
}
@Override
public void onChildViewRemoved(View parent, View child) {
}
});
}
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
//重写分发方法,不判断是否消费
for (int index = 0; index < getChildCount(); index++) {
getChildAt(index).dispatchApplyWindowInsets(insets);
}
}
return insets;
}
}
解决方法api<=20
fitSystemWindow
从19开始支持,
由于dispatchApplyWindowInsets
方法只对20及20以上api生效,而19-20之间处理分发的fitSystemWindows
方法是protected方法。考虑到20以下的设备已经比较少了,因此可以在20以下的设备上不使用透明状态栏。这样就解决了fitSystemWindow再20以下设备上可能失效的问题。
如果帮助到您的话,可以点一下喜欢。
参考
- android fitSystemWindow属性
- 一个Activity中添加多个Fragment导致fitsSystemWindows无效的问题