布局優(yōu)化之ViewStub源碼分析
源碼分析
1 @RemoteView 2 public final class ViewStub extends View { 3 private int mInflatedId; 4 private int mLayoutResource; 5 6 private WeakReference<View> mInflatedViewRef; 7 8 private LayoutInflater mInflater; 9 private OnInflateListener mInflateListener; 10 11 public ViewStub(Context context) { 12 this(context, 0); 13 } 14 15 /** 16 * Creates a new ViewStub with the specified layout resource. 17 * 18 * @param context The application's environment. 19 * @param layoutResource The reference to a layout resource that will be inflated. 20 */ 21 public ViewStub(Context context, @LayoutRes int layoutResource) { 22 this(context, null); 23 24 mLayoutResource = layoutResource; 25 } 26 27 public ViewStub(Context context, AttributeSet attrs) { 28 this(context, attrs, 0); 29 } 30 31 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) { 32 this(context, attrs, defStyleAttr, 0); 33 } 34 35 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 36 super(context); 37 38 final TypedArray a = context.obtainStyledAttributes(attrs, 39 R.styleable.ViewStub, defStyleAttr, defStyleRes); 40 mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID); 41 mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0); 42 mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID); 43 a.recycle(); 44 45 setVisibility(GONE); // 默認(rèn)不可見(jiàn) 46 setWillNotDraw(true); // 如果View不繪制任何內(nèi)容,設(shè)置這個(gè)標(biāo)記可以?xún)?yōu)化性能,默認(rèn)View沒(méi)有設(shè)置這個(gè)標(biāo)記,如果重寫(xiě)onDraw,就不要設(shè)置這個(gè)標(biāo)記 47 } 48 49 @Override 50 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 51 setMeasuredDimension(0, 0); // 測(cè)量時(shí)尺寸為0 52 } 53 54 @Override 55 public void draw(Canvas canvas) { // 不繪制內(nèi)容 56 } 57 58 @Override 59 protected void dispatchDraw(Canvas canvas) { 60 } 61 ..... 省去部分代碼 62 63 private View inflateViewNoAdd(ViewGroup parent) { 64 final LayoutInflater factory; 65 if (mInflater != null) { 66 factory = mInflater; 67 } else { 68 factory = LayoutInflater.from(mContext); 69 } // 通過(guò)inflate填充布局 70 final View view = factory.inflate(mLayoutResource, parent, false); 71 72 if (mInflatedId != NO_ID) { 73 view.setId(mInflatedId); 74 } 75 return view; 76 } 77 78 private void replaceSelfWithView(View view, ViewGroup parent) { 79 final int index = parent.indexOfChild(this); 80 parent.removeViewInLayout(this); // 移除ViewStub,后面不能在inflate 81 82 final ViewGroup.LayoutParams layoutParams = getLayoutParams(); // 獲得ViewStub的布局參數(shù) 83 if (layoutParams != null) { 84 parent.addView(view, index, layoutParams); // 把ViewStub指定的布局添加到parent中 85 } else { 86 parent.addView(view, index); 87 } 88 } 89 90 /** 91 * Inflates the layout resource identified by {@link #getLayoutResource()} 92 * and replaces this StubbedView in its parent by the inflated layout resource. 93 * 94 * @return The inflated layout resource. 95 * 96 */ 97 public View inflate() { 98 final ViewParent viewParent = getParent(); // 獲取ViewStub的parent 99 100 if (viewParent != null && viewParent instanceof ViewGroup) { 101 if (mLayoutResource != 0) { 102 final ViewGroup parent = (ViewGroup) viewParent; 103 final View view = inflateViewNoAdd(parent); 104 replaceSelfWithView(view, parent); 105 106 mInflatedViewRef = new WeakReference<>(view); 107 if (mInflateListener != null) { 108 mInflateListener.onInflate(this, view); 109 } 110 111 return view; 112 } else { 113 throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); 114 } 115 } else { 116 throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); 117 } 118 } 119 120 /** 121 * Specifies the inflate listener to be notified after this ViewStub successfully 122 * inflated its layout resource. 123 * 124 * @param inflateListener The OnInflateListener to notify of successful inflation. 125 * 126 * @see android.view.ViewStub.OnInflateListener 127 */ 128 public void setOnInflateListener(OnInflateListener inflateListener) { 129 mInflateListener = inflateListener; 130 } 131 132 /** 133 * Listener used to receive a notification after a ViewStub has successfully 134 * inflated its layout resource. 135 * 136 * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) 137 */ 138 public static interface OnInflateListener { 139 /** 140 * Invoked after a ViewStub successfully inflated its layout resource. 141 * This method is invoked after the inflated view was added to the 142 * hierarchy but before the layout pass. 143 * 144 * @param stub The ViewStub that initiated the inflation. 145 * @param inflated The inflated View. 146 */ 147 void onInflate(ViewStub stub, View inflated); 148 } 149 150 /** @hide **/ 151 public class ViewReplaceRunnable implements Runnable { 152 public final View view; 153 154 ViewReplaceRunnable(View view) { 155 this.view = view; 156 } 157 158 @Override 159 public void run() { 160 replaceSelfWithView(view, (ViewGroup) getParent()); 161 } 162 } 163 }
這是什么玩應(yīng)兒呢?其實(shí)就是一個(gè)輕量級(jí)的頁(yè)面,我們通常使用它來(lái)做預(yù)加載處理,來(lái)改善頁(yè)面加載速度和提高流暢性,ViewStub本身不會(huì)占用層級(jí),它最終會(huì)被它指定的層級(jí)取代。
在一些場(chǎng)合取代android:visibility=”gone”的用法,因?yàn)楸籫one掉的布局不斷是會(huì)同時(shí)創(chuàng)建對(duì)象的。那為什么使用ViewStub就高效呢,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
由onMeasure()方法和draw()方法可以看出, ViewStub的初始寬高都是零,所以他開(kāi)始不會(huì)占用空間,其次draw()方法也沒(méi)有執(zhí)行任何的繪制,由這兩個(gè)方法就可以看出,ViewStub的確很高效。
在代碼中要操縱ViewStub的時(shí)候,要首先使用viewstub.inflate()方法,將其所擁有的View初始化進(jìn)去。否則會(huì)報(bào)空指針錯(cuò)誤。
但ViewStub也不是萬(wàn)能的,下面總結(jié)下ViewStub能做的事兒和什么時(shí)候該用ViewStub,什么時(shí)候該用可見(jiàn)性的控制。
首先來(lái)說(shuō)說(shuō)ViewStub的一些特點(diǎn): 1. ViewStub只能Inflate一次,之后ViewStub對(duì)象會(huì)被置為空。按句話(huà)說(shuō),某個(gè)被ViewStub指定的布局被Inflate后,就不會(huì)夠再通過(guò)ViewStub來(lái)控制它了。 2. ViewStub只能用來(lái)Inflate一個(gè)布局文件,而不是某個(gè)具體的View,當(dāng)然也可以把View寫(xiě)在某個(gè)布局文件中。 基于以上的特點(diǎn),那么可以考慮使用ViewStub的情況有: 1. 在程序的運(yùn)行期間,某個(gè)布局在Inflate后,就不會(huì)有變化,除非重新啟動(dòng)。 因?yàn)閂iewStub只能Inflate一次,之后會(huì)被置空,所以無(wú)法指望后面接著使用ViewStub來(lái)控制布局。所以當(dāng)需要在運(yùn)行時(shí)不止一次的顯示和隱藏某個(gè)布局,那么ViewStub是做不到的。這時(shí)就只能使用View的可見(jiàn)性來(lái)控制了。 2. 想要控制顯示與隱藏的是一個(gè)布局文件,而非某個(gè)View。 因?yàn)樵O(shè)置給ViewStub的只能是某個(gè)布局文件的Id,所以無(wú)法讓它來(lái)控制某個(gè)View。 所以,如果想要控制某個(gè)View(如Button或TextView)的顯示與隱藏,或者想要在運(yùn)行時(shí)不斷的顯示與隱藏某個(gè)布局或View,只能使用View的可見(jiàn)性來(lái)控制。
posted on 2018-05-23 14:27 安卓筆記俠 閱讀(437) 評(píng)論(0) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)