這兩天項(xiàng)目中遇到了關(guān)于viewstub相關(guān)的crash問題, 剛好可以把相關(guān)的知識復(fù)習(xí)一下.
ViewStub是View的子類, 是一種輕量級的view, 在android性能優(yōu)化中常常使用的到.
需要注意的地方
1) viewStub屬性
使用該控件時, 主要有兩個比較重要的屬性:
- android:layoutId:設(shè)置ViewStub被inflate的布局控件Id;
- android:layout: 填充進(jìn)viewStub的布局資源id;
2) viewStub特性
- 作為一種按需"延遲化加載"的控件,viewStub本身是不可見的,類似于"占位符"的性質(zhì),在
inflate后會被指定的布局資源替換,ViewStub控件雖然也占據(jù)一定的內(nèi)存,但是相比較于其他控件, 所占用的內(nèi)存很小;- 當(dāng)viewStub實(shí)例化之前, 調(diào)用
setVisibility()也會間接調(diào)用inflate()進(jìn)行布局填充;android:layout屬性中指向的布局加載以后會替換掉viewstub本身,但是并不像Androidinclude標(biāo)簽一樣完全替代,viewStub的引用依然存在,并且可以通過viewstub的可視化操作控制view的顯示;
2) 重復(fù)inflate
viewstub只能初始化一次, 多次inflate會報(bào)
IllegalArgumentException("ViewStub must have a valid layoutResource");
異常, sourceCode如下:
[圖片上傳失敗...(image-4a814e-1552362786773)]
原因是由于調(diào)用了inflate之后viewstub就不在相關(guān)的布局中了, getParent()獲取不到父View的值了;當(dāng)然從代碼中可以看出包裹ViewStub的父控件也必須是ViewGroup的實(shí)現(xiàn)類才可以辣.
解決方案:引入一個boolean值標(biāo)識當(dāng)前viewStub是否已被初始化即可
private boolean isInflate = false;//標(biāo)記viewStub是否初始化,防止重復(fù)inflate,造成crash
mVrLoadingViewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override public void onInflate(ViewStub stub, View inflated) {
isInflate = true;
}
});
VrLoadingView mVrLoadingView;
if(!isInflate){
mVrLoadingView = (VrLoadingView) mVrLoadingViewStub.inflate();
} else {
mVrLoadingView = ((Activity) getContext()).findViewById(R.id.vr_loading_view);
}
ps:因?yàn)檫@里context一定是activity實(shí)例,所以不需要進(jìn)行擔(dān)心強(qiáng)轉(zhuǎn)失敗的問題;
3) 關(guān)于setvisibility
viewStub一經(jīng)實(shí)例化以后, 如果需要更改布局的顯示狀態(tài),可以通過viewstub.setvisibility()修改, 也可以通過設(shè)置的layoutId布局的setvisibility()方法, 替換布局文件的layout params以viewStub為準(zhǔn),如margin,padding等, 其他布局屬性以view自身為準(zhǔn);
4) viewstub的使用場景
一次加載的布局,且單個viewstub層級確實(shí)有性能資源方面的優(yōu)勢,但在如RecyclerView中使用viewStub的item view時,頁面加載極其緩慢,又或是層層嵌套的viewStub(結(jié)構(gòu)略復(fù)雜),打開一個頁面可能都要3秒左右;
處于此方面的考慮, 不建議高頻模塊使用,因?yàn)榉磸?fù)調(diào)用viewstub的inflate方法非常耗時;這種情況下建議采用include標(biāo)簽;