# 10.8 LayoutInflater(布局服務)
## 本節引言:
> 本節繼續帶來的是Android系統服務中的LayoutInflater(布局服務),說到布局,大家第一時間 可能想起的是寫完一個布局的xml,然后調用Activity的setContentView()加載布局,然后把他顯示 到屏幕上是吧~其實這個底層走的還是這個LayoutInflater,用的Android內置的Pull解析器來解析 布局。一般在Android動態加載布局或者添加控件用得較多,本節我們就來學習下他在實際開發中 的一些用法~
>
> **官方API文檔**:[LayoutInflater](http://androiddoc.qiniudn.com/reference/android/view/LayoutInflater.html)
## 1.LayoutInflater的相關介紹
### 1)Layout是什么鬼?
> 答:一個用于加載布局的系統服務,就是實例化與Layout XML文件對應的View對象,不能直接使用, 需要通過**getLayoutInflater**( )方法或**getSystemService**( )方法來獲得與當前Context綁定的 LayoutInflater實例!
### 2)LayoutInflater的用法
**①獲取LayoutInflater實例的三種方法**:
```
LayoutInflater inflater1 = LayoutInflater.from(this);
LayoutInflater inflater2 = getLayoutInflater();
LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
```
PS:后面兩個其實底層走的都是第一種方法~
**②加載布局的方法**:
> public View **inflate** (int resource, ViewGroup root, boolean attachToRoot) 該方法的三個參數依次為:
>
> ①要加載的布局對應的資源id
>
> ②為該布局的外部再嵌套一層父布局,如果不需要的話,寫null就可以了!
>
> ③是否為加載的布局文件的最外層套一層root布局,不設置該參數的話, 如果root不為null的話,則默認為true 如果root為null的話,attachToRoot就沒有作用了! root不為null,attachToRoot為true的話,會在加載的布局文件最外層嵌套一層root布局; 為false的話,則root失去作用! 簡單理解就是:**是否為加載的布局添加一個root的外層容器~!**
**③通過LayoutInflater.LayoutParams來設置相關的屬性**:
> 比如RelativeLayout還可以通過addRule方法添加規則,就是設置位置:是參考父容器呢? 還是參考子控件?又或者設置margin等等,這個由你決定~
## 2.純Java代碼加載布局
> 我們早已習慣了使用XML生成我們需要的布局,但是在一些特定的情況下,我們 需要使用Java代碼往我們的布局中動態的添加組件或者布局!
>
> 但是不建議大家完全地使用Java代碼來編寫Android頁面布局,首先一點就是代碼會多, 一多久容易亂,而且不利于業務的分離,我們還是建議使用xml來完成布局,然后通過 Java代碼對里面的組件進行修改,當然有些時候可能需要使用Java動態的來添加組件!
### **純Java代碼加載布局的流程**:
**——Step 1**:
①**創建容器**:LinearLayout ly = new LinearLayout(this);
②**創建組件**:Button btnOne = new Button(this);
**——Step 2:**
可以為容器或者組件設置相關屬性: 比如:**LinearLayout**,我們可以設置組件的排列方向:**ly.setOrientation(LinearLayout.VERTICAL);** 而組件也可以:比如Button:**btnOne.setText("按鈕1");** 關于設置屬性的方法可參見Android 的API,通常xml設置的屬性只需在前面添加:set即可,比如 **setPadding**(左,上,右,下);
**——Step 3:**
將組件或容器添加到容器中,這個時候我們可能需要設置下組件的添加位置,或者設置他的大小: 我們需要用到一個類:LayoutParams,我們可以把它看成布局容器的一個信息包!封裝位置與大小 等信息的一個類!先演示下設置大小的方法:(前面的LinearLayout可以根據不同容器進行更改)
```
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
```
很簡單,接著就到這個設置位置了,設置位置的話,通常我們考慮的只是RelativeLayout! 這個時候用到LayoutParams的addRule( )方法!可以添加多個addRule( )哦! 設置組件在父容器中的位置,
比如**設置組件的對其方式**:
```
RelativeLayout rly = new RelativeLayout(this);
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
Button btnOne = new Button(this);
rly.addView(btnOne, lp2);
```
**參照其他組件的對其方式**: (有個缺點,就是要為參考組件手動設置一個id,是**手動**!!!!) 比如:設置btnOne居中后,讓BtnTwo位于btnOne的下方以及父容器的右邊!
```
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout rly = new RelativeLayout(this);
Button btnOne = new Button(this);
btnOne.setText("按鈕1");
Button btnTwo = new Button(this);
btnTwo.setText("按鈕2");
// 為按鈕1設置一個id值
btnOne.setId(123);
// 設置按鈕1的位置,在父容器中居中
RelativeLayout.LayoutParams rlp1 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rlp1.addRule(RelativeLayout.CENTER_IN_PARENT);
// 設置按鈕2的位置,在按鈕1的下方,并且對齊父容器右面
RelativeLayout.LayoutParams rlp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rlp2.addRule(RelativeLayout.BELOW, 123);
rlp2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
// 將組件添加到外部容器中
rly.addView(btnTwo, rlp2);
rly.addView(btnOne, rlp1);
// 設置當前視圖加載的View即rly
setContentView(rly);
}
}
```
**——step 4:**
調用setContentView( )方法加載布局對象即可! 另外,如果你想移除某個容器中的View,可以調用容器.**removeView**(要移除的組件);
**運行截圖**:

## 3.Java代碼動態添加控件或xml布局
> 第二點我們講解了使用純Java代碼來加載布局,實際當中用得并不多,更多的時候是動態 的添加View控件以及動態的加載XML布局!
### **1)Java代碼動態增加View**
動態添加組件的寫法有兩種,區別在于是否需要先**setContentView(R.layout.activity_main)**; 下面演示下兩種不同寫法添加一個Button的例子:
先寫個布局文件先:**activity_main.xml**:
```
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是xml文件加載的布局"/>
</RelativeLayout>
```
**第一種不需要setContentView()加載布局文件先:**
```
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button btnOne = new Button(this);
btnOne.setText("我是動態添加的按鈕");
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.CENTER_IN_PARENT);
LayoutInflater inflater = LayoutInflater.from(this);
RelativeLayout rly = (RelativeLayout) inflater.inflate(
R.layout.activity_main, null)
.findViewById(R.id.RelativeLayout1);
rly.addView(btnOne,lp2);
setContentView(rly);
}
}
```
**第二種不需要setContentView()加載布局文件先:**
```
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOne = new Button(this);
btnOne.setText("我是動態添加的按鈕");
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.CENTER_IN_PARENT);
RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
rly.addView(btnOne,lp2);
}
}
```
**分析總結**:
> 代碼很簡單,創建按鈕后,我們又創建了一個LayoutParams對象,用來設置Button的大小, 又通過addRule()方法設置了Button的位置!
>
> **第一種方法**:通過LayoutInflate的inflate()方法加載了activity_main布局,獲得了外層容器, 接著addView添加按鈕進容器,最后setContentView();
>
> **第二種方法**:因為我們已經通過setContetView()方法加載了布局,此時我們就可以通過 findViewById找到這個外層容器,接著addView,最后setContentView()即可!
>
> 另外,關于這個setContentView( )他設置的視圖節點是整個XML的根節點!
### 2)Java代碼動態加載xml布局
接下來的話,我們換一個,這次加載的是xml文件!**動態地添加xml文件**! 先寫下主布局文件和動態加載的布局文件:
**activity_main.xml**:
```
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/btnLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="動態加載布局"/>
</RelativeLayout>
```
**inflate.xml**:
```
<?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:gravity="center"
android:orientation="vertical"
android:id="@+id/ly_inflate" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是Java代碼加載的布局" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是布局里的一個小按鈕" />
</LinearLayout>
```
接著到我們的**MainActivity.java**在這里動態加載xml布局:
```
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲得LayoutInflater對象;
final LayoutInflater inflater = LayoutInflater.from(this);
//獲得外部容器對象
final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
Button btnLoad = (Button) findViewById(R.id.btnLoad);
btnLoad.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//加載要添加的布局對象
LinearLayout ly = (LinearLayout) inflater.inflate(
R.layout.inflate_ly, null, false).findViewById(
R.id.ly_inflate);
//設置加載布局的大小與位置
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
rly.addView(ly,lp);
}
});
}
}
```
**運行截圖**:

**代碼分析**:
> **①獲取容器對象**:
>
> ```
> final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
>
> ```
>
> **②獲得Inflater對象,同時加載被添加的布局的xml,通過findViewById找到最外層的根節點**
>
> ```
> final LayoutInflater inflater = LayoutInflater.from(this);
> LinearLayout ly = (LinearLayout) inflater.inflate(R.layout.inflate_ly, null, false)
> .findViewById(R.id.ly_inflate);
>
> ```
>
> **③為這個容器設置大小與位置信息:**
>
> ```
> RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
> LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
> lp.addRule(RelativeLayout.CENTER_IN_PARENT);
>
> ```
>
> **④添加到外層容器中:**
>
> ```
> rly.addView(ly,lp);
>
> ```
## 4.LayoutInflater的inflate()方法源碼
> 最后提供下LayoutInflater的inflate()方法的源碼吧,有興趣的可以看看~,其實就是Pull解析而已~
```
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
mConstructorArgs[0] = mContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("merge can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, attrs);
} else {
View temp = createViewFromTag(name, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
rInflate(parser, temp, attrs);
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
}
return result;
}
}
```
## 本節小結:
> 本節給大家講解了一下Android中的LayoutInflater(布局服務),以及動態加載View和控件 相關的東西,相信對初學控件的朋友帶來幫助~好的,就說這么多,謝謝~ 
- 1.0 Android基礎入門教程
- 1.0.1 2015年最新Android基礎入門教程目錄
- 1.1 背景相關與系統架構分析
- 1.2 開發環境搭建
- 1.2.1 使用Eclipse + ADT + SDK開發Android APP
- 1.2.2 使用Android Studio開發Android APP
- 1.3 SDK更新不了問題解決
- 1.4 Genymotion模擬器安裝
- 1.5.1 Git使用教程之本地倉庫的基本操作
- 1.5.2 Git之使用GitHub搭建遠程倉庫
- 1.6 .9(九妹)圖片怎么玩
- 1.7 界面原型設計
- 1.8 工程相關解析(各種文件,資源訪問)
- 1.9 Android程序簽名打包
- 1.11 反編譯APK獲取代碼&資源
- 2.1 View與ViewGroup的概念
- 2.2.1 LinearLayout(線性布局)
- 2.2.2 RelativeLayout(相對布局)
- 2.2.3 TableLayout(表格布局)
- 2.2.4 FrameLayout(幀布局)
- 2.2.5 GridLayout(網格布局)
- 2.2.6 AbsoluteLayout(絕對布局)
- 2.3.1 TextView(文本框)詳解
- 2.3.2 EditText(輸入框)詳解
- 2.3.3 Button(按鈕)與ImageButton(圖像按鈕)
- 2.3.4 ImageView(圖像視圖)
- 2.3.5.RadioButton(單選按鈕)&Checkbox(復選框)
- 2.3.6 開關按鈕ToggleButton和開關Switch
- 2.3.7 ProgressBar(進度條)
- 2.3.8 SeekBar(拖動條)
- 2.3.9 RatingBar(星級評分條)
- 2.4.1 ScrollView(滾動條)
- 2.4.2 Date & Time組件(上)
- 2.4.3 Date & Time組件(下)
- 2.4.4 Adapter基礎講解
- 2.4.5 ListView簡單實用
- 2.4.6 BaseAdapter優化
- 2.4.7ListView的焦點問題
- 2.4.8 ListView之checkbox錯位問題解決
- 2.4.9 ListView的數據更新問題
- 2.5.0 構建一個可復用的自定義BaseAdapter
- 2.5.1 ListView Item多布局的實現
- 2.5.2 GridView(網格視圖)的基本使用
- 2.5.3 Spinner(列表選項框)的基本使用
- 2.5.4 AutoCompleteTextView(自動完成文本框)的基本使用
- 2.5.5 ExpandableListView(可折疊列表)的基本使用
- 2.5.6 ViewFlipper(翻轉視圖)的基本使用
- 2.5.7 Toast(吐司)的基本使用
- 2.5.8 Notification(狀態欄通知)詳解
- 2.5.9 AlertDialog(對話框)詳解
- 2.6.0 其他幾種常用對話框基本使用
- 2.6.1 PopupWindow(懸浮框)的基本使用
- 2.6.2 菜單(Menu)
- 2.6.3 ViewPager的簡單使用
- 2.6.4 DrawerLayout(官方側滑菜單)的簡單使用
- 3.1.1 基于監聽的事件處理機制
- 3.2 基于回調的事件處理機制
- 3.3 Handler消息傳遞機制淺析
- 3.4 TouchListener PK OnTouchEvent + 多點觸碰
- 3.5 監聽EditText的內容變化
- 3.6 響應系統設置的事件(Configuration類)
- 3.7 AnsyncTask異步任務
- 3.8 Gestures(手勢)
- 4.1.1 Activity初學乍練
- 4.1.2 Activity初窺門徑
- 4.1.3 Activity登堂入室
- 4.2.1 Service初涉
- 4.2.2 Service進階
- 4.2.3 Service精通
- 4.3.1 BroadcastReceiver牛刀小試
- 4.3.2 BroadcastReceiver庖丁解牛
- 4.4.2 ContentProvider再探——Document Provider
- 4.5.1 Intent的基本使用
- 4.5.2 Intent之復雜數據的傳遞
- 5.1 Fragment基本概述
- 5.2.1 Fragment實例精講——底部導航欄的實現(方法1)
- 5.2.2 Fragment實例精講——底部導航欄的實現(方法2)
- 5.2.3 Fragment實例精講——底部導航欄的實現(方法3)
- 5.2.4 Fragment實例精講——底部導航欄+ViewPager滑動切換頁面
- 5.2.5 Fragment實例精講——新聞(購物)類App列表Fragment的簡單實現
- 6.1 數據存儲與訪問之——文件存儲讀寫
- 6.2 數據存儲與訪問之——SharedPreferences保存用戶偏好參數
- 6.3.1 數據存儲與訪問之——初見SQLite數據庫
- 6.3.2 數據存儲與訪問之——又見SQLite數據庫
- 7.1.1 Android網絡編程要學的東西與Http協議學習
- 7.1.2 Android Http請求頭與響應頭的學習
- 7.1.3 Android HTTP請求方式:HttpURLConnection
- 7.1.4 Android HTTP請求方式:HttpClient
- 7.2.1 Android XML數據解析
- 7.2.2 Android JSON數據解析
- 7.3.1 Android 文件上傳
- 7.3.2 Android 文件下載(1)
- 7.3.3 Android 文件下載(2)
- 7.4 Android 調用 WebService
- 7.5.1 WebView(網頁視圖)基本用法
- 7.5.2 WebView和JavaScrip交互基礎
- 7.5.3 Android 4.4后WebView的一些注意事項
- 7.5.4 WebView文件下載
- 7.5.5 WebView緩存問題
- 7.5.6 WebView處理網頁返回的錯誤碼信息
- 7.6.1 Socket學習網絡基礎準備
- 7.6.2 基于TCP協議的Socket通信(1)
- 7.6.3 基于TCP協議的Socket通信(2)
- 7.6.4 基于UDP協議的Socket通信
- 8.1.1 Android中的13種Drawable小結 Part 1
- 8.1.2 Android中的13種Drawable小結 Part 2
- 8.1.3 Android中的13種Drawable小結 Part 3
- 8.2.1 Bitmap(位圖)全解析 Part 1
- 8.2.2 Bitmap引起的OOM問題
- 8.3.1 三個繪圖工具類詳解
- 8.3.2 繪圖類實戰示例
- 8.3.3 Paint API之—— MaskFilter(面具)
- 8.3.4 Paint API之—— Xfermode與PorterDuff詳解(一)
- 8.3.5 Paint API之—— Xfermode與PorterDuff詳解(二)
- 8.3.6 Paint API之—— Xfermode與PorterDuff詳解(三)
- 8.3.7 Paint API之—— Xfermode與PorterDuff詳解(四)
- 8.3.8 Paint API之—— Xfermode與PorterDuff詳解(五)
- 8.3.9 Paint API之—— ColorFilter(顏色過濾器)(1/3)
- 8.3.10 Paint API之—— ColorFilter(顏色過濾器)(2-3)
- 8.3.11 Paint API之—— ColorFilter(顏色過濾器)(3-3)
- 8.3.12 Paint API之—— PathEffect(路徑效果)
- 8.3.13 Paint API之—— Shader(圖像渲染)
- 8.3.14 Paint幾個枚舉/常量值以及ShadowLayer陰影效果
- 8.3.15 Paint API之——Typeface(字型)
- 8.3.16 Canvas API詳解(Part 1)
- 8.3.17 Canvas API詳解(Part 2)剪切方法合集
- 8.3.18 Canvas API詳解(Part 3)Matrix和drawBitmapMash
- 8.4.1 Android動畫合集之幀動畫
- 8.4.2 Android動畫合集之補間動畫
- 8.4.3 Android動畫合集之屬性動畫-初見
- 8.4.4 Android動畫合集之屬性動畫-又見
- 9.1 使用SoundPool播放音效(Duang~)
- 9.2 MediaPlayer播放音頻與視頻
- 9.3 使用Camera拍照
- 9.4 使用MediaRecord錄音
- 10.1 TelephonyManager(電話管理器)
- 10.2 SmsManager(短信管理器)
- 10.3 AudioManager(音頻管理器)
- 10.4 Vibrator(振動器)
- 10.5 AlarmManager(鬧鐘服務)
- 10.6 PowerManager(電源服務)
- 10.7 WindowManager(窗口管理服務)
- 10.8 LayoutInflater(布局服務)
- 10.9 WallpaperManager(壁紙管理器)
- 10.10 傳感器專題(1)——相關介紹
- 10.11 傳感器專題(2)——方向傳感器
- 10.12 傳感器專題(3)——加速度/陀螺儀傳感器
- 10.12 傳感器專題(4)——其他傳感器了解
- 10.14 Android GPS初涉
- 11.0《2015最新Android基礎入門教程》完結散花~