ViewStub - Optimized Layout
- 作用:用于優(yōu)化布局律姨,懶加載,運(yùn)行時(shí)才會(huì)加載布局臼疫。
- 使用場(chǎng)景:通常用于有些隱藏的或者特殊情況才會(huì)顯示的布局择份。
例如:一個(gè)ListView,數(shù)據(jù)為空時(shí),顯示一個(gè)布局告訴用戶
在xml中是使用:
<ViewStub
android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
/>
之前一直都不知道inflatedId有什么用烫堤,怎么用荣赶,決定看看源碼:
ViewStub的構(gòu)造器
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);
構(gòu)造很簡(jiǎn)單,讀取布局inflatedId, layout, id的屬性;調(diào)用setVisibility(GONE)鸽斟,所以ViewStub默認(rèn)是隱藏的拔创。
還有在實(shí)際使用中,會(huì)發(fā)現(xiàn)ViewStub除了
有inflatedId, layout富蓄, id這三個(gè)屬性剩燥,其他View的屬性都沒(méi)有效果。
原因是:ViewStub繼承View, 構(gòu)造器只是調(diào)用super(context),而在View中只有Content一個(gè)參數(shù)
的構(gòu)造器不會(huì)讀取其他屬性.
ViewStub的優(yōu)化原理簡(jiǎn)單粗暴, 重寫(xiě)onMeasure(), 測(cè)量時(shí)設(shè)置寬高為0格粪,重寫(xiě)draw()不繪制任何東西.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
看看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");
}
}
分析源碼得知:
首先調(diào)用getParent()獲取父布局parent躏吊,利用LayoutInflater把@layout/mySubTree這個(gè)layout加載,(為加載的layout起名為inflatedView),
把mInflatedId設(shè)置給inflatedView, int index = parent.indexOfChild(this)這一句獲取ViewStub在父布局的位置帐萎,
使用parent.removeViewInLayout(this)把ViewStub從parent中移除比伏,最后將inflatedView添加到相應(yīng)的位置并替換ViewStub.
使用ViewStub通常可以不用直接調(diào)用inflate(), 要顯示的時(shí)候直接可以調(diào)用setVisibility就可以了
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();
}
}
}
mInflatedViewRef是一個(gè)View的弱引用疆导,調(diào)用inflate()后才不為null,
第一次調(diào)用setVisibility(VISIBLE||INVISIBLE)會(huì)調(diào)用inflate(),之后會(huì)從mInflatedViewRef中
獲取inflactedView的弱引用赁项,直接setVisibility(visibility).
還可以設(shè)置OnInflateListener這個(gè)接口進(jìn)行一些inflatedView的初始化工作,
這個(gè)接口只會(huì)被調(diào)用一次。
public static interface OnInflateListener {
void onInflate(ViewStub stub, View inflated);
}
注意事項(xiàng):
- inflate()方法只能調(diào)用一次澈段,由于第一次ViewStub已經(jīng)從parent中移除悠菜,parent第二次調(diào)用會(huì)為null,
接著就會(huì)throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent") - ViewStub在xml中id為stub败富,第一次使用findViewById()可以找到ViewStub悔醋,第二次之后就找不到了。
- 當(dāng)設(shè)置了android:inflatedId="@+id/subTree"兽叮,
首次可以使用inflacte方法獲取inflactedView, View inflactedView = viewStub.inflacte(),
接著可以findViewById(R.id.subTree)獲取inflactedView.