inflate()
的時候,佈局就會被加載(替換 ViewStub)。所以,ViewStub 一直存在於視圖層次結構中直到調用了 setVisibility(int)
或 inflate()
。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();
// 初始狀態爲 GONE
setVisibility(GONE);
// 設置爲不會繪製
setWillNotDraw(true);
}
複製代碼
// 複寫了 setVisibility(int) 方法
@Override
@android.view.RemotableViewMethod public void setVisibility(int visibility) {
// private WeakReference<View> mInflatedViewRef;
// mInflatedViewRef 是對佈局的弱引用
if (mInflatedViewRef != null) {
// 若是不爲 null,就拿到懶加載的 View
View view = mInflatedViewRef.get();
if (view != null) {
// 而後就直接對 View 進行 setVisibility 操做
view.setVisibility(visibility);
} else {
// 若是爲 null,就拋出異常
throw new IllegalStateException("setVisibility called on un-referenced view");
}
} else {
super.setVisibility(visibility);
// 以前說過,setVisibility(int) 也能夠進行加載佈局
if (visibility == VISIBLE || visibility == INVISIBLE) {
// 由於在這裏調用了 inflate()
inflate();
}
}
}
複製代碼
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 {
// 若是沒有指定 LayoutInflater
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>(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");
}
}
複製代碼
@Override
@android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
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 {
}
}
複製代碼
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
mInflatedViewRef = new WeakReference<>(view);
return view;
} else {
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
}
}
}
複製代碼
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
複製代碼
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
複製代碼
/** * This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be * called and further optimizations will be performed. It is okay to have * this flag set and a background. Use with DRAW_MASK when calling setFlags. * {@hide} */
static final int WILL_NOT_DRAW = 0x00000080;
複製代碼
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initViewGroup();
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
private void initViewGroup() {
// ViewGroup doesn't draw by default
if (!debugDraw()) {
setFlags(WILL_NOT_DRAW, DRAW_MASK);
}
mGroupFlags |= FLAG_CLIP_CHILDREN;
mGroupFlags |= FLAG_CLIP_TO_PADDING;
mGroupFlags |= FLAG_ANIMATION_DONE;
mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
}
setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
mChildren = new View[ARRAY_INITIAL_CAPACITY];
mChildrenCount = 0;
mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
}
複製代碼
public View inflate() {
//獲取viewStub的父容器對象
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
//這裏是加載佈局,而且給它設置id
//佈局的加載是經過LayoutInflater解析出來的
final View view = inflateViewNoAdd(parent);
//這行代碼很重要,下面會將到
replaceSelfWithView(view, parent);
//使用弱引用
mInflatedViewRef = new WeakReference<>(view);
if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
}
return view;
} else {
//若是已經加載出來,再次inflate就會拋出異常呢
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
}
} else {
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}
}
複製代碼