从RecyclerView的子View创建来聊聊View.inflate和LayoutInflat.from(context).inflate的区别

前言

聊聊RecyclerView子View的生成为什么需要使用LayoutInflat.from(context).inflate而不是View.inflate();
最近,用RecyclerView的时候,发现一个很奇怪的现象,就是使用View.inflate()生成的View有问题。但是换成LayoutInflate.from(context)却可以,详细的可以看下这篇博客
RecyclerView的item无法充满父布局的问题
这篇文章就来看看,两个到底有什么区别。

源码分析

View.inflate源码分析


    /**
     * Inflate a view from an XML resource.  This convenience method wraps the {@link
     * LayoutInflater} class, which provides a full range of options for view inflation.
     *
     * @param context The Context object for your activity or application.
     * @param resource The resource ID to inflate
     * @param root A view group that will be the parent.  Used to properly inflate the
     * layout_* parameters.
     * @see LayoutInflater
     */
    public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }

从这里可以明显看到,走的其实也是LayoutInflater,接下来我们统一分析LayoutInflate

Layout.infalte().from()源码分析

LayoutInflater.from(context).inflate(R.layout.layout, parent, false)

#LayoutInflater.form()

    /**
     * Obtains the LayoutInflater from the given context.
     */
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

可以很明显的看到,最后调用的其实都是context.getSystemService();
Context的getSystemService实现是虚方法,具体实现是在Activity里面

//LAYOUT_INFLATER_SERVICE值为layout_inflater
    @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

从这里看出来,其实这里走的是super.getSystemService()这里我们就暂时不往下跟了,先直接给结论,免得大家看的无聊,有时候,知道结果,反问原因,更能激起人们的兴趣。
这里走的是PhoneLayoutInflater。下面代码过长,直接略过即可,直接看下面的解析,会抓住本文的重点。


    /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     *
     * @param resource ID for an XML layout resource to load (e.g.,
     *        R.layout.main_page)
     * @param root Optional view to be the parent of the generated hierarchy.
     * @return The root View of the inflated hierarchy. If root was supplied,
     *         this is the root View; otherwise it is the root of the inflated
     *         XML file.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
    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();
        }
    }
    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 {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                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(" 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
                    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);
                        }
                    }

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

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    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(parser.getPositionDescription()
                        + ": " + 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里面涉及到的东西比较多,我们抓住重点,来说明我们今天的问题。
其实大家大概也能猜到,关键问题就在于LayoutParams,看看关键代码,这里一共涉及到两个核心点:

if (TAG_MERGE.equals(name)) {

}else{
	final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    ViewGroup.LayoutParams params = null;
	//root就是传进来的parentView
   if (root != null) {
       // Create layout params that match root, if supplied
//这里涉及到一个知识点,就是如果传入的是Relativelayout,最后却添加到LinearLayout里面。这里可行吗?
//另外,这里的generateLayoutParams()其实几乎每一个ViewGroup都会重写自己的,并且生成自己的ViewGroup.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)
           //这里就是我们的核心代码1,如果有父View,但是暂时不添加,那么我们就设置params。
           temp.setLayoutParams(params);
       }
   }
   // Inflate all children under temp against its context.
   //这里是去解析当前xml文件里面最外层View的子View,递归操作,很绕。
   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.
   //这里就是我们的核心代码2,当root!=null&&允许的时候才加入。
   if (root != null && attachToRoot) {
       root.addView(temp, params);
   }
}

关键代码我已经在父View中给出了,那么,传入parentView但是未添加时,生成的LayoutParams什么时候消费呢?

关于generateLayoutParams()何时消费

这里生成的LayoutParams,毫无疑问,会在View添加到parentView的时候使用,那么,我们就来看下ViewGroup.addView():

    public void addView(View child) {
        addView(child, -1);
    }

    public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        //看到了吗,没明显,这里就是拿到了之前使用generateLayoutParams()生成的LayoutParams。
        
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }

总结

由此可见,我们使用

LayoutInflater.from(context).inflate(R.layout.item, parent, false);
View.inflate(mContext,R.layout.item,null);

的区别就在于,传入ParentView会生成一个LayoutParams,但是不传入的话,并不会生成。

以后有时间我们再来聊聊,PhoneLayoutInflater这个类时怎么来的,这涉及到的就比较多,以后专门开一章。

你可能感兴趣的:(安卓开发,源码分析,项目注意)