ViewStub應(yīng)用的場景比較少兴泥,以前用過幾次工育。時(shí)間一長又忘記了,很是苦惱搓彻。
于是把ViewStub的源碼過了一遍如绸,詳細(xì)說明了每行代碼的含義和目的嘱朽,翻譯了源碼中的注釋。
自己動眼看看吧怔接,你懂得搪泳。再多說就畫蛇添足了。
道士扼脐,請:
package?android.view;
import?android.annotation.IdRes;
import?android.annotation.LayoutRes;
import?android.content.Context;
import?android.content.res.TypedArray;
import?android.graphics.Canvas;
import?android.util.AttributeSet;
import?android.widget.RemoteViews.RemoteView;
import?com.android.internal.R;
import?java.lang.ref.WeakReference;
/**
*?ViewStub是一個(gè)不可見的岸军,0?size的View,它可以在運(yùn)行時(shí)加載一個(gè)布局瓦侮,即懶加載艰赞。
*?當(dāng)ViewStub設(shè)置為可見(visible),或者調(diào)用了ViewStub的inflate()方法時(shí)肚吏,
*?其android:layout屬性所關(guān)聯(lián)的layout文件才會被加載和生成view對象方妖。
*?此時(shí),當(dāng)前ViewStub所在的位置會被其加載的子布局霸占替換须喂;并且當(dāng)前ViewStub對象會從其父容器中移除吁断。
*?這也就是為什么ViewStub只能inflate()一次的原因。inflate一次后坞生,ViewStub對象都沒有了仔役,還怎么inflate()第二次?
*
*?下面這句話注意了:
*?被ViewStub加載的View是己,替換了ViewStub的存在又兵,當(dāng)然是添加到了ViewStub的父容器中,即原ViewStub的爸爸成了ViewStub加載的View的爸爸卒废。
*?并且在ViewStub上設(shè)置的layout參數(shù)沛厨,會轉(zhuǎn)移給ViewStub加載的View身上。
*?同樣的摔认,你也可以在ViewStub的android:inflatedId屬性上設(shè)置id逆皮,當(dāng)其加載布局時(shí),會直接把id重新設(shè)置到加載的布局根上参袱。
*?如果沒設(shè)置android:inflatedId屬性电谣,那待加載的布局根還是用以前的id屬性,沒有拉到抹蚀。
*
*?舉個(gè)栗子:
*
*
*
*android:inflatedId="@+id/這個(gè)id會在運(yùn)行時(shí)加載中剿牺,設(shè)置到下面aaaaa布局的根屬性上"
*android:layout="@layout/aaaaa,即稍后會加載且替換當(dāng)前ViewStub的布局"
*android:layout_width="120dp"
*android:layout_height="40dp"/>
*
*
*?如上面的代碼环壤,當(dāng)aaaaa布局加載后晒来,你可以使用inflatedId屬性值(即id值)找到aaaaa對象
*?或者用aaaaa內(nèi)部的根布局id找到aaaaa對象。
*
*?下面ViewStub的代碼:
*
*?????ViewStubstub=?(ViewStub)?findViewById(R.id.viewStub的id);
*?????Viewinflated=stub.inflate();
*
*
*?當(dāng)inflate()方法被調(diào)用后郑现,當(dāng)前的ViewStub將會被加載的布局替換湃崩,并且返回值為加載的布局對象荧降。
*
*?注意下面兩個(gè)屬性的定義:
*?@attr?ref?android.R.styleable#ViewStub_inflatedId?給待加載的布局設(shè)置id,也可以不設(shè)置竹习,用布局內(nèi)部設(shè)置的id值
*?@attr?ref?android.R.styleable#ViewStub_layout?待加載的布局
*/
@RemoteView
public?final?class?ViewStub?extends?View?{
private?int?mInflatedId;
private?int?mLayoutResource;
private?WeakReferencemInflatedViewRef;
private?LayoutInflater?mInflater;
private?OnInflateListener?mInflateListener;
public?ViewStub(Context?context)?{
this(context,?0);
}
/**
*?使用給定的待加載的布局文件創(chuàng)建一個(gè)ViewStub對象誊抛。
*
*?@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?TypedArraya=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();
//注意,ViewStub永遠(yuǎn)是隱藏的整陌,不會展示任何UI信息拗窃,且不占位
setVisibility(GONE);
setWillNotDraw(true);
}
/**
*?返回待加載的布局的id,如果沒有設(shè)置(即保持布局內(nèi)部根屬性上設(shè)置的id)則返回NO_ID
*
*?@return?A?positive?integer?used?to?identify?the?inflated?view?or
*?????????{@link?#NO_ID}?if?the?inflated?view?should?keep?its?id.
*
*?@see?#setInflatedId(int)
*?@attr?ref?android.R.styleable#ViewStub_inflatedId
*/
@IdRes
public?int?getInflatedId()?{
return?mInflatedId;
}
/**
*?Defines?the?id?taken?by?the?inflated?view.?If?the?inflated?id?is
*?{@link?View#NO_ID},?the?inflated?view?keeps?its?original?id.
*
*?@param?inflatedId?A?positive?integer?used?to?identify?the?inflated?view?or
*???????????????????{@link?#NO_ID}?if?the?inflated?view?should?keep?its?id.
*
*?@see?#getInflatedId()
*?@attr?ref?android.R.styleable#ViewStub_inflatedId
*/
@android.view.RemotableViewMethod
public?void?setInflatedId(@IdRes?int?inflatedId)?{
mInflatedId=inflatedId;
}
/**
*?獲取待加載的布局資源
*
*?@return?The?layout?resource?identifier?used?to?inflate?the?new?View.
*
*?@see?#setLayoutResource(int)
*?@see?#setVisibility(int)
*?@see?#inflate()
*?@attr?ref?android.R.styleable#ViewStub_layout
*/
@LayoutRes
public?int?getLayoutResource()?{
return?mLayoutResource;
}
/**
*?代碼形式設(shè)置待加載的布局資源
*
*?@param?layoutResource?A?valid?layout?resource?identifier?(different?from?0.)
*
*?@see?#getLayoutResource()
*?@see?#setVisibility(int)
*?@see?#inflate()
*?@attr?ref?android.R.styleable#ViewStub_layout
*/
@android.view.RemotableViewMethod
public?void?setLayoutResource(@LayoutRes?int?layoutResource)?{
mLayoutResource=layoutResource;
}
/**
*?Set?{@link?LayoutInflater}?to?use?in?{@link?#inflate()},?or?{@code?null}
*?to?use?the?default.
*/
public?void?setLayoutInflater(LayoutInflater?inflater)?{
mInflater=inflater;
}
/**
*?Get?current?{@link?LayoutInflater}?used?in?{@link?#inflate()}.
*/
public?LayoutInflater?getLayoutInflater()?{
return?mInflater;
}
@Override
protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{
//設(shè)置當(dāng)前ViewStub的寬高都為0泌辫,即不可見了
setMeasuredDimension(0,?0);
}
@Override
public?void?draw(Canvas?canvas)?{
//無繪制
}
@Override
protected?void?dispatchDraw(Canvas?canvas)?{
//也不調(diào)用子孩子的繪制
}
/**
*?When?visibility?is?set?to?{@link?#VISIBLE}?or?{@link?#INVISIBLE},
*?{@link?#inflate()}?is?invoked?and?this?StubbedView?is?replaced?in?its?parent
*?by?the?inflated?layout?resource.?After?that?calls?to?this?function?are?passed
*?through?to?the?inflated?view.
*
*?@param?visibility?One?of?{@link?#VISIBLE},?{@link?#INVISIBLE},?or?{@link?#GONE}.
*
*?@see?#inflate()
*/
@Override
@android.view.RemotableViewMethod
public?void?setVisibility(int?visibility)?{
//判斷mInflatedViewRef對象是否為null随夸,即判斷是否調(diào)用過下面的inflate()函數(shù)
if?(mInflatedViewRef?!=?null)?{
//已經(jīng)加載過view,直接獲取震放。
Viewview=mInflatedViewRef.get();
if?(view?!=?null)?{
//直接設(shè)置view的可見性即可宾毒,此時(shí)跟ViewStub沒什么關(guān)系了。ViewStub已經(jīng)從父容器中移除了
view.setVisibility(visibility);
}?else?{
throw?new?IllegalStateException("setVisibility?called?on?un-referenced?view");
}
}?else?{
super.setVisibility(visibility);
if?(visibility==?VISIBLE?||visibility==?INVISIBLE)?{
//如果設(shè)置了ViewStub為可見殿遂,或者占位的話诈铛,則觸發(fā)下面的inflate()函數(shù)
//注意,有了mInflatedViewRef的判斷墨礁,inflate()函數(shù)是不會調(diào)用第二次的幢竹。
//當(dāng)調(diào)用了一次inflate()函數(shù)后,當(dāng)前ViewStub對象沒有父容器了恩静,然而在調(diào)用inflate()會報(bào)異常的焕毫。
inflate();
}
}
}
/**
*?Inflates?the?layout?resource?identified?by?{@link?#getLayoutResource()}
*?and?replaces?this?StubbedView?in?its?parent?by?the?inflated?layout?resource.
*
*?@return?The?inflated?layout?resource.
*
*/
public?View?inflate()?{
//獲取到當(dāng)前ViewStub對象的父容器
final?ViewParentviewParent=getParent();
//判斷父容器是否為null
if?(viewParent?!=?null?&&?viewParent?instanceof?ViewGroup)?{
//判斷是否給ViewStub指定了android:layout=""屬性,即待加載的布局
if?(mLayoutResource?!=?0)?{
final?ViewGroupparent=?(ViewGroup)?viewParent;
final?LayoutInflater?factory;
if?(mInflater?!=?null)?{
factory=mInflater;
}?else?{
factory=LayoutInflater.from(mContext);
}
//加載指定的資源文件驶乾,注意三個(gè)參數(shù)的含義
//可參考:http://blog.csdn.net/fesdgasdgasdg/article/details/72870280
final?Viewview=factory.inflate(mLayoutResource,?parent,
false);
//如果設(shè)置了android:inflatedId=""屬性值邑飒,則把設(shè)置的id值賦給被加載的布局
if?(mInflatedId?!=?NO_ID)?{
view.setId(mInflatedId);
}
//記錄當(dāng)前ViiewStub在父容器中的位置
final?intindex=parent.indexOfChild(this);
//把當(dāng)前ViewStub對象從父容器中移除
parent.removeViewInLayout(this);
//獲取當(dāng)前ViewStub的layoutParams參數(shù)
final?ViewGroup.LayoutParamslayoutParams=getLayoutParams();
if?(layoutParams?!=?null)?{
//連同ViewStub的layoutParams參數(shù)一起,把加載獲得到的View設(shè)置到父容器相應(yīng)的位置级乐,代替了ViewStub的存在
//注意layoutParams參數(shù)包含哪些值
parent.addView(view,?index,?layoutParams);
}?else?{
parent.addView(view,?index);
}
//創(chuàng)建加載布局的弱引用疙咸,在上面setVisibility()函數(shù)中會用到
mInflatedViewRef=newWeakReference(view);
if?(mInflateListener?!=?null)?{
//布局加載成功,并且成功替換了ViewStub的位置风科,和擁有了ViewStub的資源撒轮。于是執(zhí)行回調(diào)通知調(diào)用者
mInflateListener.onInflate(this,?view);
}
//把加載到的布局對象返回給外部調(diào)用者
return?view;
}?else?{
//沒有指定待加載的布局,則報(bào)異常
throw?new?IllegalArgumentException("ViewStub?must?have?a?valid?layoutResource");
}
}?else?{
//沒有父容器丐重,則報(bào)異常
throw?new?IllegalStateException("ViewStub?must?have?a?non-null?ViewGroup?viewParent");
}
}
/**
*?設(shè)置回調(diào),當(dāng)ViewStub成功的加載了布局資源后杆查,會回調(diào)此接口進(jìn)行通知
*
*?@param?inflateListener?The?OnInflateListener?to?notify?of?successful?inflation.
*
*?@see?android.view.ViewStub.OnInflateListener
*/
public?void?setOnInflateListener(OnInflateListener?inflateListener)?{
mInflateListener=inflateListener;
}
/**
*?資源文件成功被ViewStub加載后的監(jiān)聽器
*
*?@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);
}
}