LayoutInflater 是系統提供的一個解析工具,用於將佈局XML文件實例化爲其相應的 View 對象。
調用流程
LayoutInflater 有個多個重載的inflate 方法:
android.view.LayoutInflater#inflate(int, android.view.ViewGroup)
android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)
android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup)
android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean)
最後都會執行到 android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) 方法內。
另外android.view.View#inflate 最終也是調用的LayoutInflater的inflater 方法
附一個整體的調用流程圖
源碼分析
比較常用的是
android.view.LayoutInflater#inflate(int, android.view.ViewGroup)
android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)
這兩個方法,那麼這個root 是幹嘛的,attachToRoot 的作用是啥?我們從源碼中去尋找答案。
(*注:sdk版本爲28)
/**
1. 將 xml節點解析成一個view 對象
2. <p>
3. 4. @param parser XML dom node containing the description of the view
5. hierarchy.
6. @param root 根view,如果attachToRoot 爲true,則將xml 解析得到的view 添加到此view 上。
如果false,則爲xml解析得到的view 提供LayoutParams,並不會附加到該view 上。
7. @param attachToRoot 是否將xml 解析得到的view 添加到root view上。
如果爲false,則root view 僅用作爲xml 中的頂視圖創建LayoutParmas。
8. @return 如果提供的 root 不爲null 並且attachToRoot爲true,則返回 root;否則返回解析xml得到的根view。
*/
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
// --忽略 解析相關 代碼--
View result = root;
try {
// --忽略 解析相關 代碼--
final String name = parser.getName();
// --忽略 debug 代碼--
// merge 標籤,繼續向下遞歸解析
if (TAG_MERGE.equals(name)) {
// merge 標籤必須有root,並attachToRoot爲true,否則拋異常
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
// 遞歸方法,用於向下延伸xml層次結構並實例化視圖、實例化其子視圖
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// 找出xml中的根view,賦給temp。注意,此根視圖和root 不是同一個!
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// -- 忽略debug 代碼
// 創建root 的LayoutParams
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// 如果 attachToRoot爲false,給temp 設置layoutParams。
temp.setLayoutParams(params);
}
}
// -- 忽略debug 代碼
// 解析temp 下的所有子視圖
rInflateChildren(parser, temp, attrs, true);
// -- 忽略debug 代碼
// 如果設置了root 並且attachToRoot 爲 true,則添加view 到根視圖
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// 如果root 爲空或者 attachToRoot爲false,返回在xml中找到的頂視圖。否則返回傳入的根視圖。
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription()
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
簡單總結下流程:
- 通過XmlPullParser 解析xml 佈局,獲取佈局中的根視圖 temp;
- 根據 root 是否爲null 來決定是否爲temp 創建LayoutParams;
- 根據 root 是否爲null 及 attachToRoot 是否爲true 來決定是否將 temp 添加到root 上;
- 返回正確的view。如果temp 成功添加到root 上,返回 root;否則返回temp。