LayoutInflater.inflate()详解

日常开发中,LayoutInflater的inflate()是我们经常使用的一个方法,同时也是面试中经常考察的一个知识点,它的作用是把一个xml布局填充成对应的View对象。

在LayoutInflater类中,有如下几个重载的inflate()方法:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)

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

public View inflate(XmlPullParser parser, @Nullable ViewGroup root)

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

日常开发中,使用最多的应该是前两个,它们的调用关系(箭头指向表示调用方向)如下:

四种LayoutInflater.inflate()的调用关系

从图中可以看出前三个最终调用的都是最后一个方法,因此我们只需集中精力搞清楚最后一个方法就可以了,它的源码如下:

/**
 * Inflate a new view hierarchy from the specified XML node. Throws
 * {@link InflateException} if there is an error.
 * <p>
 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
 * reasons, view inflation relies heavily on pre-processing of XML files
 * that is done at build time. Therefore, it is not currently possible to
 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
 *
 * @param parser XML dom node containing the description of the view
 *        hierarchy.
 * @param root Optional view to be the parent of the generated hierarchy (if
 *        <em>attachToRoot</em> is true), or else simply an object that
 *        provides a set of LayoutParams values for root of the returned
 *        hierarchy (if <em>attachToRoot</em> is false.)
 * @param attachToRoot Whether the inflated hierarchy should be attached to
 *        the root parameter? If false, root is only used to create the
 *        correct subclass of LayoutParams for the root view in the XML.
 * @return The root View of the inflated hierarchy. If root was supplied and
 *         attachToRoot is true, this is root; otherwise it is the root of
 *         the inflated XML file.
 */
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        // 先把root赋值给返回结果
        View result = root;

        try {
            advanceToRootNode(parser);
            // 获取xml根节点的名字,比如LinearLayout, FrameLayout, merge等。
            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("**************************");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("**************************");
            }

            if (TAG_MERGE.equals(name)) { 
                // 当xml根节点为merge时,必须有root!=null并且attachToRoot为true
                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 {
                // Temp is the root view that was found in the xml
                // temp就是xml的根节点View对象
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;

                if (root != null) {
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    // 如果root不为null,获取它的 LayoutParams
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        // attachToRoot等于false,把上面的LayoutParams设置给temp
                        temp.setLayoutParams(params);
                    }
                }

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }

                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);

                if (DEBUG) {
                    System.out.println("-----> done inflating children");
                }

                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                // attachToRoot等于true,使用上面的LayoutParams将temp添加到root上
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                // 如果root等于null或者attachToRoot等于false,直接把temp赋值给返回结果
                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(
                    getParserStateDescription(inflaterContext, attrs)
                    + ": " + 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 parser是由xml转换而来的pull解析器,用来对xml布局进行解析。

对于第二个参数@Nullable ViewGroup root,当attachToRoot为true时,root就是xml所填充成的view对象的父view;当attachToRoot为false的时候,root仅用来帮助决定xml根节点的LayoutParams,这块不理解的话,可以看下view的绘制流程,子view的大小是由父view的measureSpec和子view的宽高值决定的。

而第三个参数boolean attachToRoot,就如上面所说,用来决定xml布局和root是否存在父子布局关系。

结合上面的源码分析,可以得出如下结论

xml根节点是否为merge root attachToRoot 返回值
不为null true 返回add了xml根节点view的root对象,xml根节点view的LayoutParams(如layout_width、layout_height)根据root对象的LayoutParams生成
不为null false 返回xml根节点的view对象,它的LayoutParams(如layout_width、layout_height)根据root对象的LayoutParams生成
null true 返回没有LayoutParams信息(如layout_width、layout_height)的xml根节点View对象
null false 返回没有LayoutParams信息(如layout_width、layout_height)的xml根节点View对象
不为null(必须) true(必须) 返回的是root对象

此外,stackoverflow上的这个回答也精准阐述了本篇文章所探讨的内容,感兴趣的可以看一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容