重温基础:LayoutInflater.inflate(resource, root, attachToRoot)参数解析

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;
    }
}

核心逻辑

  1. 找到 XML 根节点 • advanceToRootNode(parser) 解析 XML 直到找到第一个 View 标签。 • 获取根 View 的 name(例如 LinearLayout、RelativeLayout 等)。
  2. 处理 <merge> 标签 • <merge> 只能在 attachToRoot = true 时使用,否则抛异常。 • 直接调用 rInflate() 解析子 View 并添加到 root。
  3. 创建根 View • 通过 createViewFromTag() 创建根 View 实例。
  4. 处理 LayoutParams • 如果 root != null,则从 root.generateLayoutParams(attrs) 生成 LayoutParams。 • 如果 attachToRoot = false,则 temp.setLayoutParams(params)。
  5. 递归解析子 View • rInflateChildren(parser, temp, attrs, true);
  6. 决定是否将 temp 添加到 root • 如果 attachToRoot = true,root.addView(temp, params);
  7. 返回值选择 • 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 的层级问题。

相关推荐
百锦再33 分钟前
Java与Kotlin在Android开发中的全面对比分析
android·java·google·kotlin·app·效率·趋势
Ya-Jun4 小时前
常用第三方库:flutter_boost混合开发
android·flutter·ios
_一条咸鱼_6 小时前
深度剖析:Android NestedScrollView 惯性滑动原理大揭秘
android·面试·android jetpack
_一条咸鱼_6 小时前
深度揭秘!Android NestedScrollView 绘制原理全解析
android·面试·android jetpack
_一条咸鱼_6 小时前
揭秘 Android CoordinatorLayout:从源码深度解析其协同工作原理
android·面试·android jetpack
_一条咸鱼_6 小时前
揭秘 Android View 的 TranslationY 位移原理:源码深度剖析
android·面试·android jetpack
_一条咸鱼_6 小时前
揭秘 Android NestedScrollView 滑动原理:源码深度剖析
android·面试·android jetpack
_一条咸鱼_6 小时前
深度揭秘:Android NestedScrollView 拖动原理全解析
android·面试·android jetpack
_一条咸鱼_6 小时前
揭秘!Android RecyclerView 预取(Prefetch)原理深度剖析
android·面试·android jetpack