关于inflate()方法

109 阅读2分钟

昨天遇到了一个bug,RecyclerView热键区域不一致,一看布局文件什么的都没有问题啊,然后仔细观察了一番,定位到了inflate()这个方法。 这个方法应该都很熟悉,在给fragment添加布局文件,给RecyclerView的adapter中为item添加布局文件时都要用到,但是其中具体参数是什么意思呢,我们来看一下源码:

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;
        View result = root;

        try {
            advanceToRootNode(parser);
            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)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }
                
                // 使用<merge>标签时,root不能为空或者attachRoot不能为false
                // 应该是因为使用merge时,由于外部也不会自动嵌套Framelayout,抛出异常

                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
                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
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                        
                        // 如果attachToRoot是false且root不为null
                        // 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.
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                    
                    // 如果root不为空且attachToRoot为true,调用root.addView()
                }

                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                if (root == null || !attachToRoot) {
                    result = temp;
                    
                    // 如果root为空,或者attachToRoot为false,直接将temp赋值给result
                }
            }

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

总结一下,分为以下几种情况:

  • inflate(R.layout.xxx,null)
    这是最简单的写法,直接将temp赋值给result,这样生成的布局就是根据R.layout.xxx返回的View。要知道,这个布局文件中的宽高属性都是相当于父布局而言的。由于没有指定parent,所以他的宽高属性就失效了,因此不管你怎么改宽高属性,都无法按我们想象的那样显示
  • inflate(R.layout.xxx,parent,false)
    相较于前者,调用了setLayoutParams(params),这里加了父布局,不管后面是true还是false,由于有了parent,布局文件的宽高属性是有依靠了,这时候显示的宽高样式就是布局文件中的那样了
  • inflate(R.layout.xxx,parent,true)
    报错了?由上面的源码我们知道,它会调用root.addView(),我们追踪一下
if (child.getParent() != null) {
    throw new IllegalStateException("The specified child already has a parent. " +
            "You must call removeView() on the child's parent first.");
}

发现这里面不能用true,那什么时候用呢,在fragment中使用,如果为true时,fragment就会被添加到父activity里了