ViewStub 源码学习

2017-09-15  本文已影响124人  看我眼前007
基于 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 源码阅读笔记

上一篇下一篇

猜你喜欢

热点阅读