陳天超
(蘭州職業(yè)技術學院電子信息工程系,甘肅蘭州730070)
在講授Android 移動開發(fā)基礎課程過程中,經(jīng)常會遇到將xml布局文件轉換為View對象的需求,Android中提供的解決方案是使用View 控件中的inflate 方法或者使用LayoutInflater(布局管理器)的inflate 方法[1-2]。由于LayoutInflater 類中有四個重載的方法[3],學生在使用Recyclerview 控件或者ListView 控件時經(jīng)常會遇到由于獲取不到相應布局參數(shù),或者由于參數(shù)設置錯誤導致給已經(jīng)有父布局的子View 對象再次添加父布局,在項目運行時就會直接崩潰。為了讓學生熟悉LayoutInflater 的作用,理解inflate 各參數(shù)的用法,正確使用inflate 方法,本文對LayoutInflater類的實例化方式及inflate方法的參數(shù)做了詳細的介紹,并提供了相應的案例。
在Android 開發(fā)中,LayoutInflater 類的實例對象不能使用new 關鍵字生成,常使用LayoutInflater.from(this)語句獲取實例化對象。LayoutInflater 類的主要作用是將解析生成的XML 布局文件創(chuàng)建生成一個View 對象,將該View 對象動態(tài)加載至指定的布局,常見的使用場景有以下幾種方式:
1)在Activity場景中的用法
LayoutInflater tflat2=getLayoutInflater();
View tv2=tflat2.inflate(R.layout.inflater2,null);
2)在Fragment場景中的用法
View tv2=tfat2.inflate(R.layout.item2,pt,false);
3)在Adapter數(shù)據(jù)適配器場景中的用法
public View getView(int p1,View cv,ViewGroup pRoot){
LayoutInflater tfat=LayoutInflater.from(pCont);
View tv=tfat.inflate(R.layout.pt,pRoot,false);
return tv; }
為了提高解析XML 文件的速度,所有解析的XML 文件都需要在構建階段(Build)進行預處理,LayoutInflater 不能加載沒有編譯的XML文件,只能加載通過XmlPullParser類解析的R文件資源,如Fragment 場景中的用法,給inflate 方法傳遞的第一個參數(shù)是R.layout.item2,LayoutInflater 的作用將布局資源轉化生成實際的Android的對象。
inflate方法有四個重載的方法,也就是說有四種表現(xiàn)形式,四種定義如下:
1)View inflate(int res2,ViewGroup pRoot)
2)View inflate(int res2,ViewGroup pRoot,boolean attRoot)
3)View inflate(XmlPullParser xmlParser,ViewGroup pRoot)
4) View inflate(XmlPullParser xmlParser, ViewGroup pRoot,boolean attRoot)
這4 個重載方法的作用是將指定的布局文件按照傳遞的參數(shù)返回相應的View 對象。在開發(fā)過程中,經(jīng)常使用方法2,通過查閱源碼可知,方法1、2、3最終都調(diào)用方法4生成指定的View對象。
有時候,也會使用View.inflate()方法,View.infl 查看該方法的源碼可知,View.inflate()方法調(diào)用了LayoutInflater 的inflate()重載方法中兩個參數(shù)的方法。調(diào)用鏈如圖1所示。

圖1 inflate方法調(diào)用鏈
在開發(fā)過程中,最常用的方法就是inflate(int res2, View‐Group pRoot,boolean attRoot)有三個參數(shù)的方法,而三個參數(shù)的方法中res2是必須傳遞的,不需要過多介紹。所以我們只需考慮pRoot 是否為null,attRoot 為true 或者false 即可,通過程序?qū)嵗齺碚f明這兩個參數(shù)的用法。
新建TestInflate 項目,InflaterActivity 使用的布局文件是in‐flater.xml,示例代碼:
android:id="@+id/ft2" android:layout_width="300dp" android:layout_height="800dp">
inflater.xml布局文件只添加了一個空的LinearLayout控件,運行時就是一個空白界面。
再建立一個布局文件,命名為add_button.xml,示例代碼:
這個布局文件中只放置了一個Button 按鈕。分四種情況使用inflater方法將add_button.xml布局文件中的按鈕動態(tài)添加到主布局文件(inflater.xml)的LinearLayout中。
InflaterActivity中的示例代碼如下所示:
protected void onCreate(Bundle state){
super.onCreate(state);
setContentView(R.layout.inflater);
LinearLayout pt2=findViewById(R.id.ft2);
LayoutInflater tfat2=LayoutInflater.from(this);
View bt2=tfat2.inflate(R.layout.add_button,null);
pt2.addView(bt2);
}
項目在夜神模擬器中運行結果如圖2所示。

圖2 將pRoot設置為null
由運行結果可知,如果pRoot設置為null,參數(shù)attRoot 將失去作用,創(chuàng)建的View對象是一個獨立的個體,將創(chuàng)建的View對象使用addView方法動態(tài)添加到相應的布局文件中。由按鈕大小可知,在button_layout.xml中設置的Button的寬度和高度屬性值沒有起作用,在布局文件中Button 的layout_width 設置為320dp,layout_height 設置為90dp,但這兩個屬性值沒有起任何作用。平時設置View 控件大小都會使用layout_width 和lay‐out_height,也會正常顯示,用戶就會默認這兩個屬性的用是設置View 控件的大小,查閱資料可知,這兩個屬性是用于設置View控件在一個布局中的大小,換句話說,View必須添加至一個布局容器中[4]。也就是說將layout_width 的屬性值設置為match_parent,就表示該View控件的寬度將會填充滿布局容器;如果將layout_width 的屬性值設置為wrap_content,則表示讓View 控件的顯示寬度剛好包含其內(nèi)容;如果將layout_width 設置為具體的數(shù)值,則View 控件的顯示的寬度會變成相應的數(shù)值,這就是Android 工程師將View 控件的兩個屬性命名為lay‐out_height和layout_width的原因。
修改InflaterActivity中的代碼:
protected void onCreate(Bundle state){
super.onCreate(state);
setContentView(R.layout.inflater);
LinearLayout pt2=findViewById(R.id.ft2);
LayoutInflater tfat2=LayoutInflater.from(this);
View butt2=tfat2.inflate(R.layout.add_button,
pt2,true);}
項目在夜神模擬器中運行結果如圖3所示。

圖3 pRoot!=null&&attRoot=true
從運行結果可以看出,創(chuàng)建的View 對象的屬性值(params)會依托于pRoot 構建[5],此時的xml 布局文件中根布局屬性有效,由于創(chuàng)建的View 對象在父布局中,所以Button 的lay‐out_width和layout_height屬性值是有效的。雖然沒有調(diào)用add‐View()方法,但已經(jīng)將創(chuàng)建好的View對象添加到父布局root中。查閱源碼可以看到,編譯器使用root.addView(temp,params)將創(chuàng)建的View對象自動添加到指定的父布局root中,所以在編程時不能再次使用addView()方法,否則就會出現(xiàn)迭代異常錯誤,如圖4所示。在ListView 控件中,要使用BaseAdapter 適配器添加數(shù)據(jù),需要將getView()方法返回的View對象提供給GridView使用,inflate方法的attachRoot參數(shù)不能設置為true。

圖4 迭代異常錯誤
修改InflaterActivity中的代碼:
protected void onCreate(Bundle state){
super.onCreate(state);
setContentView(R.layout.inflater);
LinearLayout pt2=findViewById(R.id.ft2);
LayoutInflater tfat2=LayoutInflater.from(this);
View butt2=tfat2.inflate(R.layout.add_button,
pt2,false);
pt2.addView(butt2); }
項目在夜神模擬器中運行結果如圖4所示。
運行結果與圖5 相同,創(chuàng)建的View 對象的屬性值(params)會依托于pRoot 構建,所以此時的xml 布局文件中根布局屬性有效。由于編譯器沒有把創(chuàng)建的View對象添加到指定的父布局pRoot中,在編程時需要使用addView()方法將生成的View對象添加至指定的父布局文件中。這種重載形式在編程中使用頻率比較高。

圖5 pRoot!=null&&attRoot=false
在編程中,如果省略了attRoot 參數(shù),pRoot 值不為null 時,則attRoot 參數(shù)默認為true,與第二種使用方式相同,就不再贅述。
在Android 編程中,LayoutInflater 類實例化對象不能使用new關鍵字,Android 中提供了三種實例化LayoutInflater 類對象的方式,在這三種方式中最常用的是LayoutInflater.from(this)方式。LayoutInflater類的inflate方法的基本作用是使用解析生成的XML 布局資源文件創(chuàng)建生成一個View 對象,程序員可以將該View 對象動態(tài)添加至布局文件pRoot 中,inflate 方法有四種重載形式,經(jīng)常使用方法2 即View inflate(int res2, ViewGroup pRoot, boolean attRoot),傳遞的參數(shù)是pRoot!=null&&attRoot=false,返回的View 對象不會自動添加至父布局root 中,所以在ListView 控件和RecyclerView 控件的數(shù)據(jù)適配器,getView 方法在使用inflate方法時需要將第三個參數(shù)設置為false。