布局优化ViewStub源码及在实际工作中的使用心得

原文链接: https://juejin.im/post/5a4070bc5188256dbd4b1e76

       最近工作中遇到需要使用延迟加载的功能,,就想到了ViewStub,也遇到了一些使用上ViewStub的小问题,遂对ViewStub进行了源码分析了解,其实也就是延迟了调用了inflate,addView而已.

       ViewStub是一个不可见,大小为0的视图,只有给它设置成View.Visible或者调用它的inflate()之后才会填充布局资源,所以在不需要这个View的时候占用资源少,当然我们也可以不使用ViewStub来实现某个View的显示或者隐藏,直接动态设置View或者GONE,但是即使你设置GONE,这个布局仍然会被加载,也就会消耗系统资源.

为什么ViewStub是一个不可见,大小为0的,看源码:

初始化View的时候

  private void initialize(Context context) {

    mContext = context;
    setVisibility(GONE);
    setWillNotDraw(true);
}
复制代码

在初始化的时候将ViewStub设置成了GONE,所以它是被隐藏的
setWillNotDraw则是不会绘制这个View,也就是ViewStub不会被调用onDraw



我为什么要用ViewStub?

原因1. 在一个页面初始化一个WebView组件的时候监测到上千个初始化crash,而这个WebView并不是一打开此页面就需要的,而是在某些场景下才需要使用到,而且WebView组件并不是我们这边维护的,且这个crash他们也无法马上处理,所以想到个降低crash概率的办法,到了需要加载的时候才加载WebView布局,而不是每次onCreate都初始化一次。

下面是使用示例:

布局detail_activity.xml:

<..............省略

 

 android:layout_width = "match_parent" 

 android:layout_height="wrap_parent" 

 android:layout="@layout/webview_stub"//需要延迟加载布局

 />

布局 webview_stub.xml:

 

 android:id = "+id/detail_webview"

 android:layout_width="match_parent" 

 android:layout_height="80dp" />

使用的时候:

ViewStub vs = (ViewStub)findViewById(R.id.webview_stub);

View view  = vs.inflate();

WebView webView = (WebView)view.findViewById(R.id.detail_webview);

就可以在这个时候使用WebView了。

```

原因2:当然就是提高这个Activity布局的加载速度了,初始化Activity布局的时候更简洁的布局意味着更快的加载布局速度.


关键的加载阶段:

不管你是调用ViewStub的setVisibility或者inflate函数,都会加载这个布局,其实如果布局还没加载,调用setVisibility()最后也是调用到了inflate(),来看源码

public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {
        View view = mInflatedViewRef.get();
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {//如果还没有被加载,不管是VISIBLE还是INVISIBLE都会调用inflate
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}复制代码

下面就来看inflate()

public View inflate() {
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {//父布局必须钥匙ViewGroup类型
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent;
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater;
            } else {
                factory = LayoutInflater.from(mContext);
            }
            final View view = factory.inflate(mLayoutResource, parent,
                    false);

            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }

            final int index = parent.indexOfChild(this);
            parent.removeViewInLayout(this);//把自己从布局中移除

            final ViewGroup.LayoutParams layoutParams = getLayoutParams();
            if (layoutParams != null) {//如果给ViewStub设置了布局参数则使用
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            mInflatedViewRef = new WeakReference(view);

            if (mInflateListener != null) {
                mInflateListener.onInflate(this, view);
            }

            return view;
        } else {
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}复制代码

从函数源码可以总结几点写代码时需要注意的:

1. ViewStub必须要被一个布局容器包裹

2.ViewStub调用parent.removeViewInLayout(this);把自己给移除调了,所以加载后它是不存在的,这里印证了第一点1的,如果父布局不是ViewGroup类型,就无法调用removeViewInLayout()把自己移除。

3.如果ViewStub设置了LayoutParams,则会覆盖掉layout引用的布局的LayoutParams,我是在这里出了一个问题,由于我的ViewStub引用的是一个WebView,正好在加载url 404的时候发现WebView页面变得很大,于是查看源码,没想到真是这个问题,也是把ViewStub设置成80dp则解决了在404的情况下WebView过大的问题



你可能感兴趣的:(布局优化ViewStub源码及在实际工作中的使用心得)