LayoutInflater两个inflate方法的区别

本文详细探讨了LayoutInflater的两个inflate方法的区别,特别是当root参数是否为null时的行为。重点讲解了如何通过设置root和attachToRoot参数来避免布局警告,并解释了在不同情况下,如何影响布局的加载和附加到父视图的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LayoutInflater类中有多个inflate方法,这里简要说一下:

public View inflate(int resource, ViewGroup root)

public View inflate(int resource, ViewGroup root, boolean attachToRoot)

这两个方法的区别,以及第二个方法中boolean变量的作用。

以前在使用ListView,Adapter的getView方法中,经常使用第一个方法来加载布局文件:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        convertView = layoutInflater.inflate(R.layout.list_item, null);
    }
}

这时IDE会弹出warning:

Avoid passing null as the view root (needed to resolve layout parameters on the inflated layout’s root element)

提示我们不要传递”null”作为root的参数,因为需要决定root节点的属性。

在这里,可以换用另一个inflate方法来避免这个warning:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        // 使用三个参数的inflate方法,设定root参数为getView中的parent参数,设定attachToRoot为false
        convertView = layoutInflater.inflate(R.layout.list_item, parent, false);
    }
}

使用三个参数的inflate方法,设定root参数为getView中的parent参数,设定attachToRoot为false。

这几个参数有何意义呢?我们来看一下两个inflate方法的实现。
首先两个参数的inflate方法是这样定义的:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

root参数根据标注,是可以为null的。在函数内部,实际是调用了三个参数的inflate方法。
如果我们设定root不为空,实际等于:

layoutInflater.inflate(R.layout.list_item, parent, true);

若我们给定root为null,则实际是在调用:

layoutInflater.inflate(R.layout.list_item, null, false);

那接下来就来看一下三个参数的inflate方法的内部实现:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

在这里通过给定的layout资源(R.layout.*)生成了XmlResourceParser变量,并调用另一个inflate方法,因此关键步骤就在接收XmlResourceParser参数的inflate方法中了,我们来看他的内部实现,代码很多,仅保留关键步骤:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {

            // 设定return的View(result)为参数root,后面以paramRoot代替
            View result = root;

            try {
                // 查找xml文件中的root节点,后面以xmlRoot代替
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                // 若没有开始节点,抛出异常(搜寻xmlRoot失败)
                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();


                if (TAG_MERGE.equals(name)) {
                    // 若xml头节点为merge,判断是否需要attch到paramRoot
                    // paramRoot为null,或不需要attach到paramRoot(我们传入了null参数),抛出异常,merge标签必须要有有效的root节点
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    // paramRoot有效,在其下载入xml子节点,然后直接到函数结束return处
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // xml头节点非merge,通过函数获取到xmlRoot节点
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    // paramRoot不为null,来获取其param属性
                    if (root != null) {

                        params = root.generateLayoutParams(attrs);
                        // 如果不attach到paramRoot,则将paramRoot的param设定到xmlRoot中
                        if (!attachToRoot) {
                            temp.setLayoutParams(params);
                        }
                    }

                    // 在temp下载入xmlRoot的子节点
                    rInflateChildren(parser, temp, attrs, true);

                    // 如果paramRoot不为null,并且我们设定需要attach,则使用addView,将temp添加到root中
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // 如果root为null,或者我们设定不需要attach,则直接返回xmlRoot即可;
                    // 否则需要返回paramRoot,我们在函数一开始已经默认设置了
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {

            } catch (Exception e) {

            } finally {

            }

            // 返回加载完的View
            return result;
        }
    }

简要来说:
1. 两个参数的inflate方法实际是在内部再调用三个参数的inflate方法
2. root为null,则最终要返回xml中最外层view
3. root不为null,若不需要attach,则还是返回xml中最外层view
4. root不为null,且需要attach,则通过root的addview方法,将xml整体添加到root中。如果xml最外层是merge标签,则直接在root下加载xml中的子节点。最终返回root整体这一view。

*若给定的root为null,那么就无法将root的LayoutParams设定给xml中的父view了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值