Android进阶之View.inflate和LayoutInflater.inflate方法区别

1 LayoutInflater用于加载布局

LayoutInflater用于加载布局的。加载布局的任务通常都是在Activity中调用setContentView()方法来完成的。其实setContentView()方法的内部也是使用LayoutInflater来加载布局的,只不过这部分源码是内部的。先看下LayoutInflater的基本用法,首先需要获取到LayoutInflater的实例,有两种方法可以获取到:

// 第一种写法如下:
LayoutInflater layoutInflater = LayoutInflater.from(context);
// 还有另外一种写法也可以完成同样的效果:
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 得到了LayoutInflater的实例之后就可以调用它的inflate()方法来加载布局了,如下所示:
layoutInflater.inflate(resourceId, root);

2 View.inflate和LayoutInflater.inflate效果的区别

(1)平时Recyclerview加载item中,adapter的getView方法中,我们经常用到LayoutInflater.inflate这样的方法来加载布局xml,平时一直就是这么用的,也没什么疑问。

LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);

(2)一次在加载布局时,使用了如下的方法View.inflate加载布局:

@Override
public ConsumeRechargeRecordViewHolder onCreateViewHolder_(ViewGroup parent, int viewType) {
	View itemView = View.inflate(mContext, R.layout.item_consume_recharge_record, null);
	return new ConsumeRechargeRecordViewHolder(itemView);
}

得到的效果如下,没有将item的布局文件最外层的所有layout属性设置:
这里写图片描述
(3)尝试多次调整后,使用LayoutInflater.inflate来加载布局,代码以及得到的效果截图如下,将item的布局文件最外层的所有layout属性设置(有边框):

@Override
public ConsumeRechargeRecordViewHolder onCreateViewHolder_(ViewGroup parent, int viewType) {
	View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);
    return new ConsumeRechargeRecordViewHolder(itemView);
}

这里写图片描述

3 原理分析

3.1 View.inflate与LayoutInflater.inflate的区别

虽然已经实现了效果,但心中的疑惑(为什么是这样的对比效果?)一直没解开,非常的难受,周末沉下心来研究一番,终于知道的原因所在,耶。
(1)首先看View.inflate的源码:

// View.inflate(context, resource, root)
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root); // 其实内部也是用LayoutInflater实现的:
}

(2)LayoutInflater.inflate的源码:

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

3.2 Inflate的三个参数(int resource, ViewGroup root, boolean attachToRoot)分析

(1)那对于Inflate的三个参数(int resource, ViewGroup root, boolean attachToRoot)是什么意思呢?

(2)先看源码:


public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
	    synchronized (mConstructorArgs) {
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        mConstructorArgs[0] = mContext;
        View result = root;
        try {
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
            }
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }
            final String name = parser.getName();
            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, attrs);
            } else {
                View temp = createViewFromTag(name, attrs);
                ViewGroup.LayoutParams params = null;
                if (root != null) {
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        temp.setLayoutParams(params);
                    }
                }
                rInflate(parser, temp, attrs);
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }
        } catch (XmlPullParserException e) {
            InflateException ex = new InflateException(e.getMessage());
            ex.initCause(e);
            throw ex;
        } catch (IOException e) {
            InflateException ex = new InflateException(
                    parser.getPositionDescription()
                    + ": " + e.getMessage());
            ex.initCause(e);
            throw ex;
        }
        return result;
    }
}

(3)核心部分源码如下,具体分析源码的逻辑:

	View temp = createViewFromTag(name, attrs);
	// 布局文件最外层的layout属性
    ViewGroup.LayoutParams params = null;
    if (root != null) {
	    params = root.generateLayoutParams(attrs);
	    // 3.root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置
	    if (!attachToRoot) {
	        temp.setLayoutParams(params);
	    }
    }
    rInflate(parser, temp, attrs);
    // 2.root不为null,attachToRoot设为true,则会给布局文件指定一个父布局,即root
    if (root != null && attachToRoot) {
        root.addView(temp, params);
    }
    // 1.root为null,attachToRoot将失去作用,则布局文件设置任何值都没有意义,仅仅是解析布局文件的子View
    if (root == null || !attachToRoot) {
        result = temp;
    }

(4)结论:

1. 如果root为null,attachToRoot将失去作用,则布局文件最外层设置任何值都没有意义,仅仅是解析布局文件的子View。
2. 如果root不为null,attachToRoot设为true,则会给布局文件指定一个父布局,即root。(merge作为父布局标签为什么需要attachToRoot设为true的原因)
3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true;如果root为null,attachToRoot参数为false。

4 为什么有区别?

(1)所以,当使用View.inflate(mContext, xml, null)时,传入的root为null,attachToRoot为false,attachToRoot将失去作用,则布局文件最外层设置任何值都没有意义,仅仅是解析布局文件的子View,所以得到的效果仅仅是item内部子View。
(2)那又有疑问了,设置View.inflate(mContext, xml, parent)不就可以了吗?结果是程序崩溃,为什么?
(3)因为Recyclerview中adapter的getView方法加载的布局已经设置了父布局,不需要指定一个父布局——第2点,只需要将布局文件最外层的所有layout属性进行设置——第3点,所以使用如下的方法加载生效:

View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_consume_recharge_record, parent, false);

5 学习链接

Android LayoutInflater原理分析,带你一步步深入了解View(一)

View.inflate和LayoutInflater的inflate方法区别

java.lang.IllegalStateException: ScrollView can host only one direct child at androidx.core.widget.NestedScrollView.addView(NestedScrollView.java:507) at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1131) at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72) at android.view.LayoutInflater.rInflate(LayoutInflater.java:1101) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088) at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1130) at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72) at android.view.LayoutInflater.rInflate(LayoutInflater.java:1101) at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088) at android.view.LayoutInflater.inflate(LayoutInflater.java:686) at android.view.LayoutInflater.inflate(LayoutInflater.java:505) at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:360) at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:443) at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:121) at com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:722) at com.android.tools.idea.rendering.RenderTask.lambda$inflate$9(RenderTask.java:879) at com.android.tools.idea.rendering.RenderExecutor$runAsyncActionWithTimeout$3.run(RenderExecutor.kt:194) at com.android.tools.idea.rendering.RenderExecutor$PriorityRunnable.run(RenderExecutor.kt:292) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:829)
06-06
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值