为了账号安全,请及时绑定邮箱和手机立即绑定

LayoutInflater.inflater()的正确打开方式

标签:
Android

webp

图片发自简书App

LayoutInflater在开发中使用频率很高,但是一直没有太知道LayoutInflater.from(context).inflate()的真正用法,今天就看看源码的流程。

首先来看from()的源码:

/**
 * 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中获取Context.LAYOUT_INFLATER_SERVICE所对应的系统服务。这里涉及到Context实现以及服务创建的源码,不继续深究。

重点是通常所使用的inflate()方法,比较常用的就是这两个:

  • inflate(@LayoutRes int resource, @Nullable ViewGroup root)

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

另外两个方法inflate(XmlPullParser parser, @Nullable ViewGroup root)inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

而两个参数的方法,实际也是调用了三个参数的inflate()方法,只是在三个参数传入了root!=null

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

那我们就可以直接看三个参数的inflate()方法了,其中res.getLayout(resource)这句代码,已经将我们传入的layout布局的根布局的xml属性都加载到了XmlResourceParser

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {    final Resources res = getContext().getResources();    //省略代码
    final XmlResourceParser parser = res.getLayout(resource);    try {        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}

这里其实就会发现,最后return调用的其实是inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)这个方法,所谓的四个inflate()方法,其他三个只是对这个方法的重载,主要代码还是在这个方法中实现的

这部分代码较长,以注释的形式解释代码

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {    synchronized (mConstructorArgs) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");        final Context inflaterContext = mContext;        //1.通过XmlResourceParser对象转换成AttributeSet
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        View result = root;        try {            //2.在xml中寻找根节点,如果类型是XmlPullParser.START_TAG或者XmlPullParser.END_DOCUMENT就会退出循环
            int type;            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {                // Empty
            }            //3.如果根节点类型不是XmlPullParser.START_TAG将抛出异常
            if (type != XmlPullParser.START_TAG) {                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }            final String name = parser.getName();            //4.判断根节点是否是merge标签
            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 {                //5.通过根节点创建临时的view对象
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;                if (root != null) {                    //6.如果root不为空,则调用generateLayoutParams(attrs)获取root所对应LayoutParams对象
                    params = root.generateLayoutParams(attrs);                    //是否attachToRoot
                    if (!attachToRoot) {                        //7.如果attachToRoot为false,则使用root默认的LayoutParams作为临时view对象的属性
                        temp.setLayoutParams(params);
                    }
                }                //8.inflate xml的所有子节点
                rInflateChildren(parser, temp, attrs, true);                //9.判断是否需要将创建的临时view attach到root中
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }                //10.决定方法的返回值是root还是临时view
                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 {
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }        return result;
    }
}

1中的XmlResourceParser在之前所获取的,包含了layout中跟布局的属性数据。

6,7则是很多时候使用inflate方法之后,发现xml布局设置的宽高属性不生效的部分原因,有时候在RecyclerView中添加就会这样。如果root!=nullattachToRootfalse时,创建的view则会具有自身根节点属性值,与root对应的LayoutParam

9的判断决定了创建的view是否添加到root中,而10则决定了方法返回的是root还是view

总结

根据inflate的参数不同可以获得不同的返回值

rootattachToRoot返回值
nullfalse(或者true)返回resource对应的view对象,但是xml中根节点的属性没有生效
!=nullfalse返回resource对应的view对象,并且xml中根节点的属性生效,view对象的LayoutParamrootLayoutParam对应
!=nulltrue返回root对象,对应resource创建的view对象,xml中根节点的属性生效,并且将会添加到root



作者:MrTrying
链接:https://www.jianshu.com/p/b235b2516d4f


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消