Android进阶之View.inflate和LayoutInflater.inflate方法区别

1 LayoutInflater用于加载布局

  LayoutInflater用于加载布局的。加载布局的任务通常都是在Activity中调用setContentView()方法来完成的。其实setContentView()方法的内部也是使用LayoutInflater来加载布局的,只不过这部分源码是内部的。
  我们先看下LayoutInflater的基本用法,首先需要获取到LayoutInflater的实例,有两种方法可以获取到,第一种写法如下:

LayoutInflater layoutInflater = LayoutInflater.from(context);

  还有另外一种写法也可以完成同样的效果:


LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  得到了LayoutInflater的实例之后就可以调用它的inflate()方法来加载布局了,如下所示:

layoutInflater.inflate(resourceId, root);

2 View.inflate和LayoutInflater.inflate效果的区别

  平时Recyclerview加载item中,adapter的getView方法中,我们经常用到LayoutInflater.inflate这样的方法来加载布局xml,平时一直就是这么用的,也没什么疑问。

LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);

  一次在加载布局时,使用了如下的方法View.inflate加载布局:

View itemView = View.inflate(mContext, R.layout.item_consume_recharge_record, null);

  代码以及得到的效果截图如下,没有将item的布局文件最外层的所有layout属性设置:
这里写图片描述
这里写图片描述
  尝试多次调整后,使用LayoutInflater.inflate来加载布局,代码以及得到的效果截图如下,将item的布局文件最外层的所有layout属性设置(有边框):

View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);

这里写图片描述

3 原理分析

  虽然已经实现了效果,但心中的疑惑(为什么是这样的对比效果?)一直没解开,非常的难受,周末沉下心来研究一番,终于知道的原因所在,耶。
  首先看View.inflate的源码,其实内部也是用LayoutInflater实现的:

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null);
}

  那对于Inflate的三个参数(int resource, ViewGroup root, boolean attachToRoot)又是什么意思呢?先看源码,再得出结论(使用郭神的文章得出的结论):

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        mConstructorArgs[0] = mContext;
        View result = root;
        try {
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
            }
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }
            final String name = parser.getName();
            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, attrs);
            } else {
                View temp = createViewFromTag(name, attrs);
                ViewGroup.LayoutParams params = null;
                if (root != null) {
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        temp.setLayoutParams(params);
                    }
                }
                rInflate(parser, temp, attrs);
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }
        } catch (XmlPullParserException e) {
            InflateException ex = new InflateException(e.getMessage());
            ex.initCause(e);
            throw ex;
        } catch (IOException e) {
            InflateException ex = new InflateException(
                    parser.getPositionDescription()
                    + ": " + e.getMessage());
            ex.initCause(e);
            throw ex;
        }
        return result;
    }
}

  核心部分源码如下,具体分析源码的逻辑:

    View temp = createViewFromTag(name, attrs);
    // 布局文件最外层的layout属性
    ViewGroup.LayoutParams params = null;
    if (root != null) {
        params = root.generateLayoutParams(attrs);
        // 3.root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置
        if (!attachToRoot) {
            temp.setLayoutParams(params);
        }
    }
    rInflate(parser, temp, attrs);
    // 2.root不为null,attachToRoot设为true,则会给布局文件指定一个父布局,即root
    if (root != null && attachToRoot) {
        root.addView(temp, params);
    }
    // 1.root为null,attachToRoot将失去作用,则布局文件设置任何值都没有意义,仅仅是解析布局文件的子View
    if (root == null || !attachToRoot) {
        result = temp;
    }
  1. 如果root为null,attachToRoot将失去作用,则布局文件最外层设置任何值都没有意义,仅仅是解析布局文件的子View。

  2. 如果root不为null,attachToRoot设为true,则会给布局文件指定一个父布局,即root。(merge作为父布局标签为什么需要attachToRoot设为true的原因)

  3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。

  4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true,否则相反。

4 为什么有区别?  

  所以,当使用View.inflate(mContext, xml, null)时,传入的root为null,attachToRoot为false,attachToRoot将失去作用,则布局文件最外层设置任何值都没有意义,仅仅是解析布局文件的子View,所以得到的效果仅仅是item内部子View。
  那又有疑问了,设置View.inflate(mContext, xml, parent)不就可以了吗?结果是程序崩溃,为什么?
  因为Recyclerview中adapter的getView方法加载的布局已经设置了父布局,不需要指定一个父布局——第2点,只需要将布局文件最外层的所有layout属性进行设置——第3点,所以使用如下的方法加载生效:

View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);

5 学习链接

Android LayoutInflater原理分析,带你一步步深入了解View(一)

View.inflate和LayoutInflater的inflate方法区别

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/82314122