ViewStub源码分析

Viewtub源码分析

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate

ViewStub是一个不可见的0大小的视图,可以在运行时懒加载资源

When a ViewStub is made visible, or when is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.Therefore, the ViewStub exists in the view hierarchy until or is invoked.

当ViewStub可视化或调用时,布局资源会被加载。ViewStub加载view到父视图中。因此ViewStub存在于视图层次结构中,直到或被调用。

    

ViewStub构造方法

 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context);
    
    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    //要被加载的布局id
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    //要被加载的布局
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    // ViewStub 的 Id
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    setVisibility(GONE);
    setWillNotDraw(true);
}

核心:复写setVisibility()方法

 public void setVisibility(int visibility) {
    // mInflatedViewRef 是对布局的弱引用
    if (mInflatedViewRef != null) {
        // 如果不为 null,就拿到懒加载的 View
        View view = mInflatedViewRef.get();
        if (view != null) {
            // 然后就直接对 View 进行 setVisibility 操作
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        // 之前说过,setVisibility(int) 也可以进行加载布局
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            // 因为在这里调用了 inflate()
            inflate();
        }
    }
}

inflate()关键加载方法

  • 获取父视图,如果没有指定布局,就会抛出异常

  • 获取加载的布局view,并设置view的布局id

  • 计算出 ViewStub 在 parent 中的位置,先将Viewstub移除,并将view加载到布局中

  • 将View进行弱引用

     public View inflate() {
         // 获取父视图
         final ViewParent viewParent = getParent();
         // 如果没有指定布局,就会抛出异常
         if (viewParent != null && viewParent instanceof ViewGroup) {
             if (mLayoutResource != 0) {
                 // viewParent 需为 ViewGroup
                 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);
                 // 为 view 设置 Id
                 if (mInflatedId != NO_ID) {
                     view.setId(mInflatedId);
                 }
                 // 计算出 ViewStub 在 parent 中的位置
                 final int index = parent.indexOfChild(this);
                 // 把 ViewStub 从 parent 中移除
                 parent.removeViewInLayout(this);
                 // 接下来就是把 view 加到 parent 的 index 位置中
                 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                 if (layoutParams != null) {
                     // 如果 ViewStub 的 layoutParams 不为空
                     // 就设置给 view
                     parent.addView(view, index, layoutParams);
                 } else {
                     parent.addView(view, index);
                 }
                 // mInflatedViewRef 就是在这里对 view 进行了弱引用
                 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");
         }
     }
    

ViewStub相关使用

1、特点

  • ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了
  • ViewStub layout布局不能使用merge引入

在xml使用ViewStub,使用layout引入布局文件,inflatedId是引入布局文件之后的id


通过java代码中的一系列判断可得出相关ViewStub特点

 @Override
public void onClick(View v) {

            //ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了
            if (mStubImport.getParent()!=null) {
                View view = mStubImport.inflate();
                if (view != null) {
                    if (view.getId() == R.id.panel_import) {
                        Log.e(TAG, "layout root id is act_layout_viewstub_new");
                    } else if (view.getId() == R.id.layout_viewstub_old) {
                        Log.e(TAG, "layout root id is layout_viewstub_old");
                    } else {
                        Log.e(TAG, "layout root id is anyone : " + view.getId());
                    }
                    // layoutView的root view布局 和mViewStub的布局保持一致
                    int width = view.getLayoutParams().width;
                    if (width == ViewGroup.LayoutParams.MATCH_PARENT) {
                        Log.e(TAG, "layout width is MATCH_PARENT");
                    } else if (width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                        Log.e(TAG, "layout width is WRAP_CONTENT");
                    } else {
                        Log.e(TAG, "layout width is anyone : " + width);
                    }

                }
            } else {
                Log.e(TAG, "viewStub is inflated");
            }

}

github地址

你可能感兴趣的:(ViewStub源码分析)