成员变量:
private int mInflatedId; //ViewStub传递给装载View的id
private int mLayoutResource; //装载View的布局
private WeakReference<View> mInflatedViewRef; //用弱引用包裹起来的被装载的View
构造方法:
public ViewStub(Context context) {
this(context, 0);
}
/**
* Creates a new ViewStub with the specified layout resource.
*
* @param context The application's environment.
* @param layoutResource The reference to a layout resource that will be inflated.
*/
public ViewStub(Context context, @LayoutRes int layoutResource) {
this(context, null);
mLayoutResource = layoutResource;
}
public ViewStub(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ViewStub, defStyleAttr, defStyleRes);
saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, 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);
}
比较简单,直接从xml布局中读取相应的属性,并且在构造方法中就设置为GONE,setWillNotDraw这方法看注释是声明View不做onDraw绘制的。
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
measure()测量方法直接传入0,说明ViewStub初始化就是一个0大小的View,draw()和dispatchDraw()方法什么都没做,什么都不绘制。
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
final View view = inflateViewNoAdd(parent);
replaceSelfWithView(view, parent);
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的父布局,然后在判断父布局不为null的情况下,进行加载布局生成装载View(目标View或者我们真正实际View)。
private View inflateViewNoAdd(ViewGroup parent) {
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); //加载布局设置好布局的ID。
}
return view;
}
private void replaceSelfWithView(View view, ViewGroup parent) {
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);
}
}
接着获取到当前ViewStub的位置index,移除掉ViewStub,添加装载View到index位置上,也就是替换原来的ViewStub。
回调方法:
/**
* Listener used to receive a notification after a ViewStub has successfully
* inflated its layout resource.
*
* @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
*/
public static interface OnInflateListener {
/**
* Invoked after a ViewStub successfully inflated its layout resource.
* This method is invoked after the inflated view was added to the
* hierarchy but before the layout pass.
*
* @param stub The ViewStub that initiated the inflation.
* @param inflated The inflated View.
*/
void onInflate(ViewStub stub, View inflated);
}
在inflate生成装载View的时候进行回调。