當(dāng)我們想將一個(gè)xml布局轉(zhuǎn)化成view時(shí),一般用到LayoutInflater.inflate方法,這個(gè)方法的重載有如下四種形式:
1. public View inflate(int resource, ViewGroup root)
2. public View inflate(int resource, ViewGroup root, boolean attachToRoot)
3. public View inflate(XmlPullParser parser, ViewGroup root)
4. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
至于何時(shí)root不可以傳null,何時(shí)可以傳null,這里就不重點(diǎn)贅述了。這篇文章只是重點(diǎn)分析attachToRoot這個(gè)參數(shù)。
當(dāng)attachToRoot為true時(shí)
將一個(gè)xml布局文件放入組件
layout_parent
主要它的顏色為白色
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical" >
</LinearLayout>
layout_btn
注意顏色為紅色
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000" >
</Button>
MainActivity的主要代碼為:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_parent);
final LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.layout_btn, linearLayout, true);
}
查看效果如下:

這里設(shè)置的attachToRoot為true,查看源碼,當(dāng)不設(shè)置這個(gè)值的時(shí)候
/** * Inflate a new view hierarchy from the specified xml resource.
Throws * {@link InflateException} if there is an error.
* @param resource ID for an XML layout resource to load (e.g., *<code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy.
* @return The root View of the inflated hierarchy. If root was supplied, * this is the root View; otherwise it is the root of the inflated * XML file.
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root){
return inflate(resource, root, root != null);
}
通過源碼可知,當(dāng)不傳入attachToRoot這個(gè)參數(shù)時(shí),attachToRoot的值是根據(jù)root是否為null來決定的。
將xml布局放入自定義控件中
layout_custome_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0f0"
android:text="Hello World!" >
</TextView>
</LinearLayout>
CustomerLayout
package com.ziv.test;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class CustomLinear extends LinearLayout {
public CustomLinear(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomLinear(Context context) {
super(context); init();
}
private void init() {
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.layout_custome_view, this);
}
}
MainActivity的主要代碼:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(newCustomLinear(this));
}
運(yùn)行效果:

當(dāng)attachToRoot為false時(shí):
當(dāng)attachToRoot為false時(shí),意思是不將xml文件生成的view添加到root中,相當(dāng)于根root毫無關(guān)系,獨(dú)立的一個(gè)view,這時(shí)就需要單獨(dú)的靠用addView()添加到要顯示的位置上,實(shí)例如下:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_parent);
final LinearLayout linearLayout = (LinearLayout)findViewById(R.id.layout_parent);
LayoutInflater inflater = LayoutInflater.from(this);
Button button = (Button)inflater.inflate(R.layout.layout_btn, outLayout, false);
linearLayout.addView(button);
}
//只有addView了才會(huì)立即添加到LinearLayout中去
運(yùn)行一下,效果一樣:

但是android中有幾種情況是不需要手動(dòng)設(shè)置attachToRoot設(shè)置為true,才會(huì)添加到root中去的。
第一種情況:
在fragment中的onCreateView中
package com.ziv.test;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class CustomFragment extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup
container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_btn, container, false);
return view;
}
}
第二種情況
在activity中:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_parent);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.ll, new CustomFragment());
fragmentTransaction.commit();
}
“add View”的邏輯由FragmentTransaction 進(jìn)行處理了,如果我們這里將attachToRoot設(shè)置為true,就會(huì)有IllegalStateException.

所以組件自己有“add View”的機(jī)制,我們就不要再畫蛇添足了。 可能會(huì)有疑問既然沒有將創(chuàng)建的View 添加到root中,為什么還要添加root參數(shù)呢?直接使用
View view = inflater.inflate(R.layout.layout_btn, container, false);
不是更簡(jiǎn)單嗎?如果這樣寫,lint會(huì)給出警告 Avoid passing null as the view root (needed to resolve layout parameters on the inflated layout’s root element)。 xml布局文件在解析成View的時(shí)候,需要root的布局信息(View的布局參數(shù)要受到root的限制)。如果不填寫root,使用xml布局文件生成View的時(shí)候就會(huì)使用默認(rèn)的LayoutParams.而這個(gè)不一定是符合要求的,View可能比設(shè)定的要小
有些時(shí)候無法明確的知道View的root,eg.當(dāng)你實(shí)例化一個(gè)AlterDialog.
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_parent);
LayoutInflater inflater = LayoutInflater.from(this);
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View customView = inflater.inflate(R.layout.layout_btn, null);
dialogBuilder.setView(customView);
dialogBuilder.show();
}
第三種情況:
ListView或者RecyclerView中的adapter中的getView方法和onCreateViewHolder也是這種情況,輸入自動(dòng)addView。
綜上所述:
- 如果知道root,一定要傳,盡量避免傳null
- 當(dāng)不需要將布局文件生成的View添加到組件中時(shí)(組件有其自身的添加邏輯),attachToRoot設(shè)置成false.
- 當(dāng)View已經(jīng)添加到一個(gè)root中時(shí),attachToRoot設(shè)置成false.
- 自定義組件應(yīng)該將attachToRoot設(shè)置成true.s