Android LayoutInflater

一、LayoutInflater

參考
Android LayoutInflater詳解
LayoutInflate的使用

在實際開發中LayoutInflater這個類還是非常有用的,它的作用類似于findViewById()。不同點是LayoutInflater是用來找res/layout/下的xml布局文件,并且實例化;而findViewById()是找xml布局文件下的具體widget控件(如Button、TextView等)。
具體作用:
1、對于一個沒有被載入或者想要動態載入的界面,都需要使用LayoutInflater.inflate()來載入
2、對于一個已經載入的界面,就可以使用Activiyt.findViewById()方法來獲得其中的界面元素。
LayoutInflater作用是將layout的xml布局文件實例化為View類對象。

setContentView()一旦調用, layout就會立刻顯示UI;而inflate只會把Layout形成一個以view類實現成的對象,有需要時再用setContentView(view)顯示出來。一般在activity中通過setContentView()將界面顯示出來,但是如果在非activity中如何對控件布局設置操作了,這就需要LayoutInflater動態加載。
<pre>
//setContentView(R.layout.main);

LayoutInflater inflate = LayoutInflater.from(this);
View view = inflate.inflate(R.layout.main,null);
setContentView(view);
</pre>

LayoutInflater在Android中是“擴展”的意思,作用類似于findViewById(),不同的是LayoutInflater是用來獲得布局文件對象的,而findViewById()是用來獲得具體控件的。LayoutInflater經常在BaseAdapter的getView方法中用到,用來獲取整個View并返回。

LayoutInflater 是一個抽象類,在文檔中如下聲明:
public abstract class LayoutInflater extends Object

獲得 LayoutInflater 實例的三種方式:

  1. LayoutInflater inflater = getLayoutInflater();//調用Activity的getLayoutInflater()
  2. LayoutInflater inflater = LayoutInflater.from(context);
  3. LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    其實,這三種方式本質是相同的,從源碼中可以看出這三種方式最終本質是都是調用的Context.getSystemService()
二、inflater

以下參考Layout Inflation不能這么用

現在我們來仔細看看Android框架關于動態載入布局的場景。

1.Adapter是最常用的場景,我們經常需要使用LayoutInflater來自定義ListView(通過重寫getView()方法),具體的方法簽名是這樣的:
getView(int position, View convertView, ViewGroup parent)
2.Fragment也會用到inflation操作,通過onCreateView()方法創建view的時候會用到。這個方法的簽名是這樣的:
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

不知你有沒有注意到這一點,每次Framework需要你去載入一個布局文件時,都會傳入一個ViewGroup參數(最后需要綁定到的根視圖)所以你想想看,如果我做綁定操作的話,為什么要給你一個ViewGroup參數呢?事實證明父視圖在這個inflation操作過程中是很重要的,它會計算被載入的XML在根元素中的LayoutParams,如果傳入null話,就等于是告訴框架“我不知道載入的View要放到哪個父視圖中”。
問題在于,android:layout_xxx屬性會在父視圖對象中被重新計算,結果就是所有你定義的LayoutParams都會被忽略掉(因為沒有已知的父視圖對象)。然后你就納悶“為什么框架會忽略掉我自己定義的布局屬性呢?還是去StackOverFlow上看看,提一個bug吧”。
如果沒有設置LayoutParams,那么最終ViewGroup也會給你生成一個默認的屬性,幸運的話(很多時候),這些默認的設置正好和你在XML文件中定義的一樣……所以你就察覺不到其實已經出現問題了。

以下參考Android LayoutInflater深度解析 給你帶來全新的認識

ListView的Item的布局文件:
<pre>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/id_btn"
android:layout_width="120dp"
android:layout_height="120dp" >
</Button>
</pre>

ListView的適配器:
<pre>
public View getView(int position, View convertView, ViewGroup parent)
{

    ViewHolder holder = null;  
    if (convertView == null)  
    {  
        holder = new ViewHolder();  
        convertView = mInflater.inflate(R.layout.item, null);  

// convertView = mInflater.inflate(R.layout.item, parent ,false);
// convertView = mInflater.inflate(R.layout.item, parent ,true);
holder.mBtn = (Button) convertView.findViewById(R.id.id_btn);
convertView.setTag(holder);
} else
{
holder = (ViewHolder) convertView.getTag();
}

    holder.mBtn.setText(mDatas.get(position));  

    return convertView;  
}  

</pre>

圖1
圖2

圖3直接報錯
<pre>
FATAL EXCEPTION: main
java.lang.UnsupportedOperationException:
addView(View, LayoutParams) is not supported in AdapterView
</pre>

由上面getView三行代碼的變化,產生3個不同的結果,可以看到
inflater(resId, null )的確不能正確處理寬高的值,但是inflater(resId,parent,false)并非和inflater(resId, null )效果一致,它可以看出完美的顯示了寬和高。而inflater(resId,parent,true)報錯了

分析源碼已經可以看出:
Inflate(resId , null ) 只創建temp ,返回temp
Inflate(resId , parent, false )創建temp,然后執行temp.setLayoutParams(params);返回temp
Inflate(resId , parent, true ) 創建temp,然后執行root.addView(temp, params);最后返回root
由上面已經能夠解釋:
Inflate(resId , null )不能正確處理寬和高是因為:layout_width,layout_height是相對了父級設置的,必須與父級LayoutParams一致。而此temp的getLayoutParams為null
Inflate(resId , parent,false ) 可以正確處理,因為temp.setLayoutParams(params);這個params正是root.generateLayoutParams(attrs);得到的。
Inflate(resId , parent,true )不僅能夠正確的處理,而且已經把resId這個view加入到了parent,并且返回的是parent,和以上兩者返回值有絕對的區別。
圖3中的報錯是因為源碼中調用了root.addView(temp, params);而此時的root是我們的ListView,ListView為AdapterView的子類:直接看AdapterView的源碼:
<pre>
@Override
public void addView(View child) {
throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
}
</pre>

可以看到這個錯誤為啥產生了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容