原文出處:http://blog.csdn.net/guolin_blog/article/details/12921889
** 作為一名剛接觸Android的,對LayoutInflater不怎么熟悉,因為加載布局的任務通常都是在Activity中調(diào)用setContentView()方法來完成的。其實setContentView()方法的內(nèi)部也是使用LayoutInflater來加載布局的,只不過這部分源碼是internal的,不太容易查看到。**
如何獲取LayoutInflater實例
LayoutInflater inflater = getLayoutInflater(); // 調(diào)用 Activity 的 getLayoutInflater();
LayoutInflater inflater = LayoutInflater.from(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
其實第二種就是第三種的簡單寫法,只是Android給我們做了一下封裝而已。
加載布局
layoutInflater.inflate(resourceId, root);
inflate()方法一般接收兩個參數(shù),第一個參數(shù)就是要加載的布局id,第二個參數(shù)是指給該布局的外部再嵌套一層父布局,如果不需要就直接傳null。這樣就成功成功創(chuàng)建了一個布局的實例,之后再將它添加到指定的位置就可以顯示出來了。
項目實例
MainActivity對應的布局文件activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</LinearLayout>
button_layout.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" >
</Button>
通過LayoutInflater來將button_layout這個布局添加到主布局文件的LinearLayout里
MainActivity.java
public class MainActivity extends Activity {
private LinearLayout mainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
LayoutInflater layoutInflater = LayoutInflater.from(this);
View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);
mainLayout.addView(buttonLayout);
}
}
這里首先獲取到了LayoutInflater的實例,然后調(diào)用它的inflate()方法來加載button_layout這個布局,最后調(diào)用LinearLayout的addView()方法將它添加到LinearLayout中。
由此可見,LayoutInflater技術廣泛應用于需要動態(tài)添加View的時候,比如在ScrollView和ListView中,經(jīng)常都可以看到LayoutInflater的身影。
inflater方法的重載
inflate(int resource, ViewGroup root, boolean attachToRoot)
- 如果root為null,attachToRoot將失去作用,設置任何值都沒有意義。
- 如果root不為null,attachToRoot設為true,則會給加載的布局文件的指定一個父布局,即root。
- 如果root不為null,attachToRoot設為false,則會將布局文件最外層的所有l(wèi)ayout屬性進行設置,當該view被添加到父view當中時,這些layout屬性會自動生效。
- 在不設置attachToRoot參數(shù)的情況下,如果root不為null,attachToRoot參數(shù)默認為true。
現(xiàn)在我們把button_layout.xml里面的layout_width和layout_height屬性賦值,使按鈕變大。
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="80dp"
android:text="Button" >
</Button>
執(zhí)行程序后無效果,這是為什么呢?
layout_width和layout_heigth其實是用于設置View在布局中的大小的,也就是說,首先View必須存在于一個布局中,之后如果將layout_width設置成match_parent表示讓View的寬度填充滿布局,如果設置成wrap_content表示讓View的寬度剛好可以包含其內(nèi)容,如果設置成具體的數(shù)值則View的寬度會變成相應的數(shù)值。
再來看一下我們調(diào)用的inflater函數(shù),第二個root參數(shù)我們設定為null。所以在布局button_layout.xml設定的屬性是沒有作用的。解決方法:
private LinearLayout mainLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View buttonView =
LayoutInflater.from(this).inflate(R.layout.button_layout, mainLayout);
ViewParent viewParent = buttonView.getParent();
Log.d(getClass().getSimpleName(), viewParent + "");
mainLayout.addView(buttonView);
Log.d(getClass().getSimpleName(), viewParent + "");
}
上述代碼會拋出異常:java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.當inflater方法的第三個參數(shù)為true時,已經(jīng)為button_layout.xml添加了父布局,再調(diào)用mainLayout.addView(buttonView);時會拋出該異常。
正確的解決方法:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View buttonView = LayoutInflater.from(this).inflate(R.layout.button_layout, mainLayout);
ViewParent viewParent = buttonView.getParent();
Log.d(getClass().getSimpleName(), viewParent + "");
}
或者:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View buttonView = LayoutInflater.from(this).inflate(R.layout.button_layout, mainLayout,false);
ViewParent viewParent = buttonView.getParent(); Log.d(getClass().getSimpleName(), viewParent + "");
mainLayout.addView(buttonView);
Log.d(getClass().getSimpleName(), viewParent + "");
}
這時候有個疑問,平時在Activity中指定布局文件的時候,最外層的那個布局是可以指定大小,layout_width和layout_height都是有作用的。確實,這主要是因為,在setContentView()方法中,Android會自動在布局文件的最外層再嵌套一個FrameLayout,所以layout_width和layout_height屬性才會有效果。修改MainActivity中的代碼,如下所示:
public class MainActivity extends Activity{
private LinearLayout mainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
ViewParent viewParent = mainLayout.getParent();
Log.d(getClass().getSimpleName(), viewParent + "");
}
}
可以看到,這里通過findViewById()方法,拿到了activity_main布局中最外層的LinearLayout對象,然后調(diào)用它的getParent()方法獲取它的父布局,再通過Log打印出來。結果如下圖所示:
D/MainActivity: android.widget.FrameLayout{152fb4d3 V.E..... ......I. 0,0-0,0 #1020002 android:id/content}
這個FrameLayout是Android系統(tǒng)自動為我們加上的。任何一個Activity中顯示的界面其實主要都由兩部分組成,標題欄和內(nèi)容布局。標題欄就是在很多界面頂部顯示的那部分內(nèi)容,比如剛剛我們的那個例子當中就有標題欄,可以在代碼中控制讓它是否顯示。而內(nèi)容布局就是一個FrameLayout,這個布局的id叫作content,我們調(diào)用setContentView()方法時所傳入的布局其實就是放到這個FrameLayout中的,這也是為什么這個方法名叫作setContentView(),而不是叫setView()。

異常 java.lang.IllegalStateException The specified child already has a parent. You must call removeView() on the child's parent first:
拋出這類異常的最可能的就是安裝的設備中還有相同包名的包。
實例