基于 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 使用
-
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(); } } }
-
直接调用 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.pngViewStub 用到 LayoutInflater 加载布局文件,LayoutInflater 的用法可以参考LayoutInflater 源码阅读笔记
网友评论