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。