美文网首页
关于 Android LayoutInflater

关于 Android LayoutInflater

作者: mtvVip | 来源:发表于2017-06-06 16:15 被阅读0次

这两天看到 LayoutInflater 的分析文章,有点问题,于是自己看了一下源码,动手试了试验证一下。正所谓纸上得来终觉浅。

源码简单说明


Layoutinflater 生成 View 有4个重载的方法:

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

前面 3 个方法最终都会调用第 4 个方法,重载的惯用伎俩,呵呵。所以看第4个是什么样的。
源码:呃,看起来有点多,但其实关键的就几句,其他都是一些 debug 日志什么的。

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

整个流程下来大概就是:使用XmlPullParser 解析 layout 文件,然后根据标签名使用 createViewFromTag 方法创建 View 实例。使用 rInflateChildren(parser, temp, attrs, true)递归创建子View。接下来就是根据 root 和 attachToRoot 参数的情况,看是否要创建 LayoutParams,是否调用 root 的 addView 方法将 View 放到 root 里面去。多说一句,这里的 root 就是我们从 inflater 传递进来的ViewGroup 参数。 最后如果 attachToRoot 是 true 的话,返回 root,否则返回新创建的 View。
整个看下来:关键就在于参数 root 和 attachToRoot

  • root 等于 null,不生成 LayoutParams 不添加到 root
  • root 不等于 null,生成 LayoutParams。这时候如果 attachToRoot 是 fasle,表示不添加到 root,将LayoutParams 设置给生成的 View,并将 View 返回。如果attachToRoot 是 true,则调用root.addView(temp, params);将 View 添加到 root,并将 root 返回。

第一种情况,返回的 View 没有设置 LayoutParams,第二种情况则有。接下来试一下


代码测试

代码:root 等于 null

        Button view = (Button) LayoutInflater.from(this).inflate(R.layout.button, null);
//        layout.addView(view);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        Log.d("debug", "null: " + (params == null));

运行结果:


第一种

代码:root 不等于 null,attachToRoot = false

        LinearLayout layout = (LinearLayout) findViewById(R.id.m_content);
        Button view = (Button) LayoutInflater.from(this).inflate(R.layout.button, layout, false);
//        layout.addView(view);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        Log.d("debug", "null: " + (params == null));
        
        //打印LayoutParams log
        logParams(params);

运行结果:


2.1

代码:root 不等于 null,attachToRoot = true

        LayoutInflater.from(this).inflate(R.layout.button, layout, true);
//        layout.addView(view);
        Button view = (Button) layout.getChildAt(0);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        Log.d("debug", "null: " + (params == null));

        //打印LayoutParams log
        logParams(params);

运行结果:


2.2

从上图可以看出,Button 已经被添加到 LinearLayout 里去了

另外的重载方法

源码:

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

可以看出:不设置 attacheToRoot ,值默认为 root != null 所以。。。

其他

View 类也有个 inflater 方法,内部也是用 LayoutInflater 实现的。

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

参考文章:
1.Android LayoutInflater原理分析,带你一步步深入了解View(一)
2.2

相关文章

网友评论

      本文标题:关于 Android LayoutInflater

      本文链接:https://www.haomeiwen.com/subject/oligfxtx.html