美文网首页
view系列源码疑惑分析之fitsSystemWindows作用

view系列源码疑惑分析之fitsSystemWindows作用

作者: 暴走的小青春 | 来源:发表于2019-05-19 15:18 被阅读0次

fitsSystemWindows这个属性最开始出现的地方是关于沉浸式状态栏的时候,用法也是很简单粗暴,在xml的根布局设置这个属性为true,你所要的内容就适配了屏幕,我们来看下文档中对他的解释:

Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this >view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.

经过具体实验,具体的作用就是你的contentview是否忽略actionbar,title,屏幕的底部虚拟按键,将整个屏幕当作可用的空间。
正常情况,contentview可用的空间是去除了actionbar,title,底部按键的空间后剩余的可用区域;

但有时候写了此属性会不生效,所以我们由此来深入的分析一下:
首先要从viewRootimp的performTraversals说起,也是任何绘制的由来,里面有个dispatchApplyInsets方法

  void dispatchApplyInsets(View host) {
        host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
    }
//view的dispatchApplyWindowInsets方法

  public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
        try {
            mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;
            if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
                return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets);
            } else {
                return onApplyWindowInsets(insets);
            }
        } finally {
            mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS;
        }
    }
//viewgroup的dispatchApplyWindowInsets方法
  @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;
    }

首先这个host当然就是decorview了,首先调用view的dispatchApplyWindowInsets方法,当没有mOnApplyWindowInsetsListener时调用自己的onApplyWindowInsets方法也就是

    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
            // We weren't called from within a direct call to fitSystemWindows,
            // call into it as a fallback in case we're in a class that overrides it
            // and has logic to perform.
            if (fitSystemWindows(insets.getSystemWindowInsets())) {
                return insets.consumeSystemWindowInsets();
            }
        } else {
            // We were called from within a direct call to fitSystemWindows.
            if (fitSystemWindowsInt(insets.getSystemWindowInsets())) {
                return insets.consumeSystemWindowInsets();
            }
        }
        return insets;
    }

这里一个PFLAG3_FITTING_SYSTEM_WINDOWS这个flag由于已经被ActionBarOverlayLayout设置过了,所以直接走的下面这个方法

   private boolean fitSystemWindowsInt(Rect insets) {
        if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
            mUserPaddingStart = UNDEFINED_PADDING;
            mUserPaddingEnd = UNDEFINED_PADDING;
            Rect localInsets = sThreadLocal.get();
            if (localInsets == null) {
                localInsets = new Rect();
                sThreadLocal.set(localInsets);
            }
            boolean res = computeFitSystemWindows(insets, localInsets);
            mUserPaddingLeftInitial = localInsets.left;
            mUserPaddingRightInitial = localInsets.right;
            internalSetPadding(localInsets.left, localInsets.top,
                    localInsets.right, localInsets.bottom);
            return res;
        }
        return false;
    }

对于设置FITS_SYSTEM_WINDOWS的view做了以下判断,毕竟有很多view设置了这个属性,怎么判断被消耗了呢,就要走到
computeFitSystemWindows方法里了

  protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
        if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
                || mAttachInfo == null
                || ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0
                        && !mAttachInfo.mOverscanRequested)) {
            outLocalInsets.set(inoutInsets);
            inoutInsets.set(0, 0, 0, 0);
            return true;
        } else {
            // The application wants to take care of fitting system window for
            // the content...  however we still need to take care of any overscan here.
            final Rect overscan = mAttachInfo.mOverscanInsets;
            outLocalInsets.set(overscan);
            inoutInsets.left -= overscan.left;
            inoutInsets.top -= overscan.top;
            inoutInsets.right -= overscan.right;
            inoutInsets.bottom -= overscan.bottom;
            return false;
        }
    }

看到了设置padding的关键代码可以看到几个判断,简单来说如果我们在xml的根部view设置了fitSystemWindows这个属性或者是非全屏条件下设置了这个属性的view。

简单来说就是

1.设置了全屏后只有xml的根view才能让fitsystemwindow的属性生效
即自动调整布局
2.不设置全屏,只要设置了fitsystemwindow的view就能消耗此属性,在decoview添加的view中 com.android.internal.R.layout.screen_simple就是被率先添加的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

可以看到在content外面的一层的Linearlayout就写了此属性,消费的意义就是LinearLayout设置了paddingTop为25dp,避开了状态栏,而LinearLayout的子view FrameLayout会比LinearLayout少了25dp.

相关文章

网友评论

      本文标题:view系列源码疑惑分析之fitsSystemWindows作用

      本文链接:https://www.haomeiwen.com/subject/tptqzqtx.html