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