LayoutInflater.inflate() 方法是 Android 中加载 XML 布局文件并生成 View 对象的关键方法。它会解析 XML 布局文件,并返回相应的 View 对象。不同的参数组合会影响 View 的 层级关系、LayoutParams 以及是否自动添加到父容器。
方法定义
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
resource(@LayoutRes int) :要加载的 XML 布局资源 ID,如 R.layout.item_view。 root(@Nullable ViewGroup) :指定 View 的 父容器,主要用于 LayoutParams 设定。可以是 null。 attachToRoot(boolean):是否将 View 直接添加到 root: • true:直接添加到 root,返回 root。 • false:不添加到 root,但 root 可能会影响 LayoutParams。 • root == null:不会影响 LayoutParams,返回独立的 View。
inflate() 方法的几种使用情况
1、root != null, attachToRoot = true(直接添加到 root)
适用场景:适用于 静态布局(如 Activity、Fragment 的 onCreateView()),希望 View 直接插入 root。示例:
kotlin
LinearLayout parentLayout = findViewById(R.id.parentLayout);
View view = LayoutInflater.from(this).inflate(R.layout.item_view, parentLayout, true);
效果:
- view 被直接添加 到 parentLayout,不需要 addView(view)。
- inflate() 方法返回的 View 不是
item_view
的 TextView,而是parentLayout
本身。
2、 root != null, attachToRoot = false(不会自动添加到 root)
适用场景:适用于 RecyclerView、ViewPager 等,需要手动管理 View 的 LayoutParams 和 addView()。
示例:
kotlin
LinearLayout parentLayout = findViewById(R.id.parentLayout);
View view = LayoutInflater.from(this).inflate(R.layout.item_view, parentLayout, false);
parentLayout.addView(view); // 需要手动 addView()
效果:
- view 不会自动添加 到 parentLayout,但 view 的 LayoutParams 可能会继承 parentLayout 的布局参数。
- 需要 parentLayout.addView(view) 手动添加。
RecyclerView是我们平时经常用的控件,为什么 对应Adapter#onCreateViewHolder
里 attachToRoot 必须为 false呢,如下:
kotlin
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new ViewHolder(view);
}
这是因为RecyclerView 自己管理 View 的 addView() 和 LayoutParams,如果改成 attachToRoot = true
,View 会被 提前添加 到 RecyclerView,导致崩溃!
3、root == null, attachToRoot = false(创建独立 View)
适用场景:适用于 Dialog、Toast、PopupWindow 等不需要绑定父容器的情况。示例:
kotlin
View view = LayoutInflater.from(this).inflate(R.layout.item_view, null, false);
这个 view 只是 一个独立的 View,没有 LayoutParams,适用于 Toast、Dialog:
kotlin
Toast toast = new Toast(context);
toast.setView(view);
toast.show();
注:因为root == null,当上面的view被其他ViewGroup.addView()且没有指定LayoutParams 时,XML 布局的第一层中的layout_width、layout_height
都会失效,其他layout_
相关的属性也会失效。
源码分析
查看 LayoutInflater 的 inflate() 方法:
kotlin
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
XmlResourceParser parser = getContext().getResources().getLayout(resource);
return inflate(parser, root, attachToRoot);
}
//最终会调用:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// 1. 找到 XML的根节点
advanceToRootNode(parser);
final String name = parser.getName();
// 2. 如果根标签是 <merge>
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, inflaterContext, attrs, false);
} else {
// 3. 创建 XML 根 View
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// 4. 如果 root != null,创建 `LayoutParams`
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
// 5. 递归解析并添加子 View
rInflateChildren(parser, temp, attrs, true);
// 6. 是否将 `temp` 添加到 `root`
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// 7. 返回值选择
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
throw new InflateException(e.getMessage(), e);
} catch (Exception e) {
throw new InflateException(getParserStateDescription(inflaterContext, attrs) + ": " + e.getMessage(), e);
} finally {
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
return result;
}
}
核心逻辑
- 找到 XML 根节点 • advanceToRootNode(parser) 解析 XML 直到找到第一个 View 标签。 • 获取根 View 的 name(例如 LinearLayout、RelativeLayout 等)。
- 处理
<merge>
标签 •<merge>
只能在 attachToRoot = true 时使用,否则抛异常。 • 直接调用 rInflate() 解析子 View 并添加到 root。 - 创建根 View • 通过 createViewFromTag() 创建根 View 实例。
- 处理 LayoutParams • 如果 root != null,则从 root.generateLayoutParams(attrs) 生成 LayoutParams。 • 如果 attachToRoot = false,则 temp.setLayoutParams(params)。
- 递归解析子 View • rInflateChildren(parser, temp, attrs, true);
- 决定是否将 temp 添加到 root • 如果 attachToRoot = true,root.addView(temp, params);
- 返回值选择 • attachToRoot = true:返回 root(因为 temp 已经被 root 添加) 。 • attachToRoot = false:返回 temp(temp 没有被添加到 root)。
总结
root | attachToRoot | 结果 |
---|---|---|
非null | true | View 自动添加 到 root,返回 root |
非null | false | View 不会自动添加 到 root,但 View 可能继承 root 的 LayoutParams,返回 View 本身 |
null | false | View 独立存在,不会有 LayoutParams,适用于 Toast/Dialog |
LayoutInflater.inflate() 是 Android UI 组件动态加载的关键方法,这几种方式各有应用场景,合理选择 inflate() 的使用方式可以避免 View 的层级问题。