inflate方法是LayoutInflater類中的一個公有方法,該方法共有4個重載的方法原型,不過看源碼便知,以下方法1,2,3最後兜兜轉轉都調用了方法4:java
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)android
public View inflate(XmlPullParser parser, @Nullable ViewGroup root)數組
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)dom
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)函數
parser:一個xml dom節點,在該節點中包含了待生成的視圖層次結構的描述信息源碼分析
root:一個可選的ViewGroup對象,當attachToRoot爲true時,root爲生成的view樹的parent;當attachToRoot爲false時,root僅爲一個爲生成的視圖層次提供參數的對象佈局
attachToRoot:是否將生成的視圖層次附加到root參數上,false:root僅僅用於爲xml文件的根節點建立正確的參數子類spa
返回生成的view樹的根節點rest
(注:參數parser不能爲空,若爲空,會提示錯誤):code
Case1:當參數root == null時,函數調用爲inflate(parser, null)
Case2:當參數root != null && attachToRoot == false時,函數調用爲inflate(parser, root, false)
Case3:當參數root != null && attachToRoot == true時,函數調用爲inflate(parser, root, true)
那麼如上幾種參數場景都會有怎樣的返回值呢?咱們接下來一塊兒看看inflate方法內部的實現吧!
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final Context inflaterContext = mContext; /* * attrs:從給定的parser中得到的一個xml屬性集,咱們通常不會直接使用該類型參數 * 因此不用管,只需知道attrs中包含了咱們傳進去的layout文件中所定義的參數屬性值便好 */ final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; /* result爲待返回的View對象 */ View result = root; /* * 這裏有一段代碼,未貼出來 功能:尋找xml中的根節點,找到時將起始節點的名稱賦值給name 若沒有找到則拋異常 */ if (TAG_MERGE.equals(name)) { // 當根節點爲merge節點時的處理 if (root == null || !attachToRoot) { throw new InflateException( "<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } /* * 這是一個遞歸方法,從xml根節點開始向下遞歸,先初始化該層view, * 而後初始化下一層view,當整個view樹都生成完畢時調用onFinishInflate() * 接下來咱們就能夠經過findViewById方法找到具備id屬性的view了 */ rInflate(parser, root, inflaterContext, attrs, false); } else { // 根節點不爲merge節點時的處理 /* * temp爲xml文件中的根節點view * createViewFromTag方法用於根據給定的參數集attrs爲名爲name的tag標籤生成一個view * 由上文可知,此處name爲xml文件的根節點名稱,因此temp爲xml的root view */ final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { // 注意:root非空時的處理 // Create layout params that match root, if supplied /* * generateLayoutParams是ViewGroup中提供的一個public方法 * 用於根據給定的參數集attrs生成一個LayoutParams * LayoutParams通常由view使用,用於告訴view的parent,他們想要的layout樣式 * 一個基本的LayoutParams通常只用於描述他們的size信息,即view的width和height * * 注意:此處經過root來調用generateLayoutParams獲得了一個LayoutParams對象 */ params = root.generateLayoutParams(attrs); if (!attachToRoot) { /* * 當root != null && attachToRoot == false時(即前面說的Case2) * 用params所指代的LayoutParams爲tamp設置佈局參數 */ temp.setLayoutParams(params); } } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); /* * 當root != null && attachToRoot == true時(即前面說的Case3) * 將根節點爲temp的view樹添加到root上,temp的佈局參數爲params */ if (root != null && attachToRoot) { root.addView(temp, params); } /* * 當root == null || !attachToRoot時(前面說的Case1和Case2屬於這種狀況) * 將temp賦值給result */ if (root == null || !attachToRoot) { result = temp; } } /* * 返回獲得的view樹的根節點 * * 注意:不一樣狀況下的result值是不同的 * 當root == null時,result爲xml根節點view,但並未爲該view設置LayoutParams值 * 當root != null && attachToRoot == false時 * result爲xml根節點view,且根據xml文件中提供的參數值爲該view設置了LayoutParams值 * 當root != null && attachToRoot == true時,result爲初始值root */ return result; } }
假設root 的xml文件爲layout_root,待infalte的xml文件爲layout_child:
layout_root:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_root" android:orientation="vertical" android:layout_width="300dp" android:layout_height="400dp" android:background="@color/forestgreen" > <TextView android:id="@+id/tv_root_ll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LL and root" android:layout_gravity="center_horizontal" android:textSize="20sp" /> </LinearLayout>
layout_child:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_child" android:orientation="vertical" android:layout_width="200dp" android:layout_height="200dp" android:background="@color/violet" > <TextView android:id="@+id/tv_child_ll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LL and child" android:layout_gravity="center_horizontal" android:textSize="20sp" /> </LinearLayout>
因此由4中的分析可知,3中3種case下面的返回值分別爲:
Case1:返回id爲ll_child的LinearLayout視圖,該view的LayoutParams爲null
Case2:返回id爲ll_child的LinearLayout視圖,該view的LayoutParams爲xml文件layout_child中提供的參數,即width和height分別爲200dp和200dp
Case3:返回id爲ll_root的LinearLayout視圖,該view的LayoutParams爲xml文件layout_root中提供的參數,即width和height分別爲300dp和400dp,同時因爲這是一個vertical的LinearLayout,因此會將layout_child所表明的view樹添加到id爲tv_child_ll的TextView下面
下面根據上面三種case分別進行實驗,查看顯示效果
Case1:
private void caseOne(){ childView = inflater.inflate(R.layout.layout_child, null); setContentView(childView); }
實驗結果:
結果分析:由5可知,該狀況下返回的是一個LayoutParams == null,根節點id爲ll_child的LinearLayout,至於爲何顯示效果如上圖所示則涉及到setContentView方法以及Android的窗口加載原理,簡而言之就是在Android加載佈局文件時,若是view的LayoutParams爲null,則默認將該view的width和height賦值爲MATCH_PARENT,具體請參考個人下一篇博客《Android 從setContentView談Activity界面的加載過程》
Case2:
private void caseTwo(){ rootView = (ViewGroup)inflater.inflate(R.layout.layout_root,null); childView = inflater.inflate(R.layout.layout_child, rootView, false); setContentView(childView); }
實驗結果:
結果分析:由上圖能夠看出,該狀況與5中的分析相符
Case3:
private void caseThree(){ rootView = (ViewGroup)inflater.inflate(R.layout.layout_root,null); childView = inflater.inflate(R.layout.layout_child, rootView, true); setContentView(childView); }
實驗結果:
結果分析:由上圖能夠看出,該狀況與5中的分析相符