解决fitSystemWindow有时失效的问题

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无效的问题

你可能感兴趣的:(解决fitSystemWindow有时失效的问题)