从源代码角度分析ViewStub 疑问与原理
生活随笔
收集整理的這篇文章主要介紹了
从源代码角度分析ViewStub 疑问与原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、提出疑問
ViewStub比較簡單。之前文章都提及到《Android 性能優化 三 布局優化ViewStub標簽的使用》。可是在使用過程中有一個疑惑,究竟是ViewStub上設置的參數有效還是在其包含的layout中設置參數有效?假設不明確描寫敘述的問題,能夠看下下面布局偽代碼。 res/layout/main.xml <LinearLayout ><ViewStubandroid:id="@+id/viewstub"android:layout_width="100dip"android:layout_marginTop="100dip"android:layout_height="wrap_content"android:layout="@layout/sub_layout"/></LinearLayout>res/layout/sub_layout.xml <TextViewandroid:layout_width="50dip"android:layout_marginTop="50dip"android:layout_height="wrap_content"android:text="ViewStub中包括的TextVeiw"/>
上面的代碼中width終于效果是100dip還是50dip?marginTop是100dip還是50dip?帶著這個問題一起看下Android 5.0源代碼看看ViewStub原理。
為了便于把ViewStub與其infalte()載入出來的android:layout視圖做個區分,下文中針對前者統一命名“ViewStub視圖”。后者命名“被 載入視圖”,僅為了描寫敘述統一并不一定是專業名稱。
二、分析ViewStub源代碼
讓ViewStub有兩種方式一種是調用ViewStub.inflate() 第二種是設置ViewStub.setVisibility(View.VISIBLE); 事實上第二種方式依舊是調用的infalte方法,能夠看例如以下ViewStub源代碼。 @Override@android.view.RemotableViewMethodpublic 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();}}}
ViewStub復寫了setVisibility方法,并在當中調用infalte方法。以下來看此方法源代碼
public final class ViewStub extends View {......public View inflate() {final ViewParent viewParent = getParent(); // 1 為什么能夠直接獲取父視圖?// ViewStub的父視圖必須是ViewGroup的子類if (viewParent != null && viewParent instanceof ViewGroup) {if (mLayoutResource != 0) { // ViewStub必須設置android:layout屬性final ViewGroup parent = (ViewGroup) viewParent;final LayoutInflater factory;if (mInflater != null) {factory = mInflater;} else {factory = LayoutInflater.from(mContext);}// 2 inflate被載入視圖 final View view = factory.inflate(mLayoutResource, parent,false);if (mInflatedId != NO_ID) {view.setId(mInflatedId);}// 從父視圖中獲取當前ViewStub在父視圖中的位置final int index = parent.indexOfChild(this);// 當前ViewStub也是個View只不過用來占位。所以先把占位的ViewStub視圖刪除parent.removeViewInLayout(this);// 3 此處獲取的是ViewStub上面設置的參數final ViewGroup.LayoutParams layoutParams = getLayoutParams();if (layoutParams != null) {parent.addView(view, index, layoutParams);} else {parent.addView(view, index);}// 目的是在復寫的setVisibility方法中使用// 由于ViewStub.setVisibility操作的是被載入視圖并不是當前ViewStub視圖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");}}...... }
以下說下源代碼中列出的幾點。
?1. 為什么能夠直接獲取父視圖?
ViewStub 繼承自View其自身就是一個視圖,其調用getParent()能夠從父類View上入手、
public class View { public final ViewParent getParent() {return mParent;}void assignParent(ViewParent parent) {if (mParent == null) {mParent = parent;} else if (parent == null) {mParent = null;} else {throw new RuntimeException("view " + this + " being added, but"+ " it already has a parent");}} }
從View的源代碼中獲取到,改動mParent參數的僅有assignParent方法且View中并未調用此方法,以下查看下其子類ViewGroup是否有調用。
public class ViewGroup { public void addView(View child, int index, LayoutParams params) {......addViewInner(child, index, params, false);}private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {...... // tell our childrenif (preventRequestLayout) {child.assignParent(this);} else {child.mParent = this;}......} }
從上面源代碼能夠看到在addView方法中會調用addViewInner,當中調用child.assignParent(this);,把自己全部子視圖mParent都設置成當前ViewGroup。 從這一點也能夠看出,ViewStub本身是一個View且載入的時候就已經加入到視圖樹中(View Tree)中,僅接著有另外一個問題既然頁面顯示的時候ViewStub已經被加入到界面上。為什么有看不到ViewStub視圖呢?
疑問:為什么ViewStub盡管是懶載入。可是其自身是一個視圖且展示界面就會加入到視圖樹中,為什么看不到ViewStub?
public final class ViewStub extends View {public ViewStub(Context context) {initialize(context);}private void initialize(Context context) {mContext = context;setVisibility(GONE); // 初始化時把自己設置為隱藏setWillNotDraw(true);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(0, 0); // 全部子視圖都設置為寬高為0}@Overridepublic void draw(Canvas canvas) { // 不正確自身與子視圖進行繪制}@Overrideprotected void dispatchDraw(Canvas canvas) {} }
從以上源代碼能夠看出ViewStub用盡全部辦法讓自己加入到視圖樹上是不顯示ViewStub自身。
2. inflate被載入視圖 再來看下載入android:layout視圖的源代碼。 final View view = factory.inflate(mLayoutResource, parent,?false); 能夠看到通過infalte方法記載的。其三個參數(int resource, ViewGroup root, boolean attachToRoot),各自是: mLayoutResource :?設置的android:layout的值 parent : 通過getParent()獲取即ViewStub的父視圖 false :?attachToRoot設置為false說明忽略androd:layout中根節點的layoutParams參數,即width=50dip與margin50dip
3. 視圖加入ViewStub.getLayoutParams參數 此處源代碼的是獲取ViewStub.getLayoutParams參數設置到anroid:layout載入的視圖上。?即width=100dip與marginTop=100dip生效。
三、總結 開頭的疑問的答案。inflate出來的視圖width=100dip與marginTop=100dip而android:layout視圖中設置的width50dip和marginTop=50dip失效,等于沒有設置。
ViewStub的原理簡單描寫敘述是 1. ViewStub本身是一個視圖。會被加入到界面上,之所以看不到是由于其源代碼設置為隱藏與不繪制。 2. 當調用infalte或者ViewStub.setVisibility(View.VISIBLE);時(兩個都使用infalte方法邏輯),先從父視圖上把當前ViewStub刪除。再把載入的android:layotu視圖加入上 3. 把ViewStub?layoutParams?加入到載入的android:layotu視圖上。而其根節點layoutParams?設置無效。 4. ViewStub是指用來占位的視圖,通過刪除自己并加入android:layout視圖達到懶載入效果
轉載于:https://www.cnblogs.com/bhlsheji/p/5174505.html
總結
以上是生活随笔為你收集整理的从源代码角度分析ViewStub 疑问与原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MAC设置——企业邮箱标准版
- 下一篇: 优化Linux内核参数/etc/sysc