美文网首页
ViewStub 源码学习

ViewStub 源码学习

作者: 看我眼前007 | 来源:发表于2017-09-15 14:09 被阅读124次
    基于 6.0.1_r10

    作用

    ViewStub 可以用来优化布局,实现布局的懒加载。

    ViewStub 不占用屏幕空间,大小为0

    只有当调用 inflate 或者 viewStub.setVisibility(View.VISIBLE||INVISIBLE) 时才会加载布局

    构造方法

    ViewStub 5 个构造方法,层层包裹,最终会调用下面方法

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context);
    
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();
    
        setVisibility(GONE);
        setWillNotDraw(true);
    }
    

    可以看出 ViewStub 构造方法

    1. 组件本身 setVisibility(GONE) 
    2. setWillNotDraw(true);
    所以 ViewStub 不会绘制出来,不占用屏幕空间。
    

    ViewStub 使用

    1. setVisibility()
      ViewStub 重载了 View 的 setVisibility() 方法

        @Override
       @android.view.RemotableViewMethod
       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 {
               super.setVisibility(visibility);
               if (visibility == VISIBLE || visibility == INVISIBLE) {
                   inflate();
               }
           }
       }
      
    2. 直接调用 inflate()

       public View inflate() {
       final ViewParent viewParent = getParent();
      
       if (viewParent != null && viewParent instanceof 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) {
                   parent.addView(view, index, layoutParams);
               } else {
                   parent.addView(view, index);
               }
      
               mInflatedViewRef = new WeakReference<View>(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. setVisibility 显示布局的时候,会调用 inflate
    3. inflate 只能调用一次,会把加载到的 View 放到弱引用 mInflatedViewRef 中
       两次调用 inflate ,会因为第一次把 ViewStub 从 parentView 中移除,导致出现 crash
    4. inflate 调用时会用真正的布局,替换 ViewStub (加载到父布局同样的位置)
    5. inflate 调用以后,setVisibility 使用 mInflatedViewRef 控制布局显示/隐藏
    

    以上就是 ViewStub 的全部,代码比较简单注释也比较全面。

    总结一个简单的流程图如下

    viewStub_01.png

    ViewStub 用到 LayoutInflater 加载布局文件,LayoutInflater 的用法可以参考LayoutInflater 源码阅读笔记

    参考资料

    developer 官方文档

    ViewStub 源码

    LayoutInflater 源码阅读笔记

    相关文章

      网友评论

          本文标题:ViewStub 源码学习

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