源碼分析 這是什麼玩應兒呢?其實就是一個輕量級的頁面,我們通常使用它來做預載入處理,來改善頁面載入速度和提高流暢性,ViewStub本身不會占用層級,它最終會被它指定的層級取代。 在一些場合取代android:visibility=”gone”的用法,因為被gone掉的佈局不斷是會同時創建對象的。那 ...
源碼分析
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); // 預設不可見 46 setWillNotDraw(true); // 如果View不繪製任何內容,設置這個標記可以優化性能,預設View沒有設置這個標記,如果重寫onDraw,就不要設置這個標記 47 } 48 49 @Override 50 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 51 setMeasuredDimension(0, 0); // 測量時尺寸為0 52 } 53 54 @Override 55 public void draw(Canvas canvas) { // 不繪製內容 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 } // 通過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的佈局參數 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 }
這是什麼玩應兒呢?其實就是一個輕量級的頁面,我們通常使用它來做預載入處理,來改善頁面載入速度和提高流暢性,ViewStub本身不會占用層級,它最終會被它指定的層級取代。
在一些場合取代android:visibility=”gone”的用法,因為被gone掉的佈局不斷是會同時創建對象的。那為什麼使用ViewStub就高效呢,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
由onMeasure()方法和draw()方法可以看出, ViewStub的初始寬高都是零,所以他開始不會占用空間,其次draw()方法也沒有執行任何的繪製,由這兩個方法就可以看出,ViewStub的確很高效。
在代碼中要操縱ViewStub的時候,要首先使用viewstub.inflate()方法,將其所擁有的View初始化進去。否則會報空指針錯誤。
但ViewStub也不是萬能的,下麵總結下ViewStub能做的事兒和什麼時候該用ViewStub,什麼時候該用可見性的控制。
首先來說說ViewStub的一些特點: 1. ViewStub只能Inflate一次,之後ViewStub對象會被置為空。按句話說,某個被ViewStub指定的佈局被Inflate後,就不會夠再通過ViewStub來控制它了。 2. ViewStub只能用來Inflate一個佈局文件,而不是某個具體的View,當然也可以把View寫在某個佈局文件中。 基於以上的特點,那麼可以考慮使用ViewStub的情況有: 1. 在程式的運行期間,某個佈局在Inflate後,就不會有變化,除非重新啟動。 因為ViewStub只能Inflate一次,之後會被置空,所以無法指望後面接著使用ViewStub來控制佈局。所以當需要在運行時不止一次的顯示和隱藏某個佈局,那麼ViewStub是做不到的。這時就只能使用View的可見性來控制了。 2. 想要控制顯示與隱藏的是一個佈局文件,而非某個View。 因為設置給ViewStub的只能是某個佈局文件的Id,所以無法讓它來控制某個View。 所以,如果想要控制某個View(如Button或TextView)的顯示與隱藏,或者想要在運行時不斷的顯示與隱藏某個佈局或View,只能使用View的可見性來控制。