日常开发中很多地方都见到了LayoutInflater.from().inflate()方法去将一个布局文件的内容填充为一个View,特别是inflate()这个方法,这个方法的参数有布局文件id,root和attachToRoot,那么这个root和attachToRoot参数有什么作用呢?
本篇文章就是一篇详细分析 root和attachToRoot参数 对于填充的View有什么影响,当然最主要是为了我以后方便复(预)习的。
日常调用代码展示
日常开发过程中是通过类似下面的代码来将布局文件填充出来的,并且inflate()方法的返回值是一个View对象。
LayoutInflater.from(this).inflate(R.layout.layout_inflater, findViewById(R.id.ll), true);
点击inflate()方法,查看这个函数的root和attachToRoot参数对填充出来的View有什么影响。
查看inflate()函数源码
发现有两个return,
- 通过调用tryInflatePrecompiled(resource, res, root, attachToRoot)方法 创建View并return;
- 通过重载的inflate()方法创建View并return。
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) + ")");
}
// 1、tryInflatePrecompiled()函数返回View对象
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
XmlResourceParser parser = res.getLayout(resource);
try {
// 2、通过重载函数inflate()返回View对象
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
-
那么究竟是哪个方法创建了View对象呢?
这里可以直接的说出结论,默认的情况下肯定是调用重载函数inflate()来创建出View对象的。
简单分析默认情况不调用tryInflatePrecompiled(resource, res, root, attachToRoot)创建View对象
在tryInflatePrecompiled(resource, res, root, attachToRoot)函数的代码块中,有一个判断,这是判断是这个方法是否工作的开关,默认情况下mUseCompiledView是false,所以默认的情况下,不会调用该方法创建View对象。
private @Nullable
View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
boolean attachToRoot) {
if (!mUseCompiledView) {
return null;
}
// 省略了好多的创建View的代码
.......
}
那么创建出View的方法就是重载的inflate()方法了,
查看inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)方法
这里主要分析root和attachToRoot参数对创建出来的View的影响,对于View是如何创建出来这个问题,先放一放,等后面兴致来了再分析。
下面的代码块是整个inflate()方法的简约版本,删除了很多的暂时不需要了解的代码,也方便看的时候,能够看到满满的全是重点,就很nice。
核心代码块展示
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
.... 省略了代码 ....
// 1、声明返回的View对象 result,并赋予初始值为 root
View result = root;
// 2、createViewFromTag(root, name, inflaterContext, attrs)并不是本文重点,只需要关心这个temp对象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
/*
*下面的代码进行分段,每一段有每一段的逻辑处理
*/
// 第一段:设置temp的params,设置之后temp的最外层宽高才会生效
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
.... 省略了代码 ....
// 第二段:是否需要把temp添加到root上
// 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);
}
// 第三段:result是否需要重新赋值
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
.... 省略了代码 ....
// 返回result对象
return result;
}
排列组合root和attachToRoot的传参方式,并代入查看对生成的View影响
先按照排列组合的方式,列举出所有的传参方式,结果如下:
| root取值 | attachToRoot取值 |
|---|---|
| null | false |
| null | true |
| not null | false |
| not null | true |
根据上面的组合方式,一组组的分析,这些参数对View的影响
当root = null , attachToRoot = false
- 通过代码块+注释查看执行流程
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
.... 省略了代码 ....
// 1、声明返回的View对象 result,并赋予初始值为 root
View result = root;
// 2、createViewFromTag(root, name, inflaterContext, attrs)并不是本文重点,只需要关心这个temp对象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
/*
*下面的代码进行分段,每一段有每一段的逻辑处理
*/
// root = null , attachToRoot = false 所以不会执行第一段
// 第一段:设置temp的params,设置之后temp的最外层宽高才会生效
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
.... 省略了代码 ....
// root = null , attachToRoot = false 所以不会执行第二段
// 第二段:是否需要把temp添加到root上
// root = null,所以此段代码不执行
// 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 = null , attachToRoot = false 所以会执行第三段,将temp赋值给result并返回该result
// 第三段:result是否需要重新赋值
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
.... 省略了代码 ....
// 返回result对象
return result;
}
-
分析结果
能够看到,最后创建出来的View没有设置params就使用,所以被创建出来的布局,最外层设置的宽高是无效的,并最终返回了创建出来的View对象。
当root = null , attachToRoot = true
- 通过代码块+注释查看执行流程
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
.... 省略了代码 ....
// 1、声明返回的View对象 result,并赋予初始值为 root
View result = root;
// 2、createViewFromTag(root, name, inflaterContext, attrs)并不是本文重点,只需要关心这个temp对象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
/*
*下面的代码进行分段,每一段有每一段的逻辑处理
*/
// root = null , attachToRoot = true不会执行第一段
// 第一段:设置temp的params,设置之后temp的最外层宽高才会生效
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
.... 省略了代码 ....
// root = null , attachToRoot = true 不会执行第二段
// 第二段:是否需要把temp添加到root上
// root = null,所以此段代码不执行
// 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 = null , attachToRoot = true 会执行第三段,将temp赋值给result并返回该result
// 第三段:result是否需要重新赋值
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
.... 省略了代码 ....
// 返回result对象
return result;
}
-
分析结果
能够看到,最后创建出来的View没有设置params就使用,所以被创建出来的布局,最外层设置的宽高是无效的,并最终返回了创建出来的View对象。
当root != null , attachToRoot = false
- 通过代码块+注释查看执行流程
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
.... 省略了代码 ....
// 1、声明返回的View对象 result,并赋予初始值为 root
View result = root;
// 2、createViewFromTag(root, name, inflaterContext, attrs)并不是本文重点,只需要关心这个temp对象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
/*
*下面的代码进行分段,每一段有每一段的逻辑处理
*/
// 当root != null , attachToRoot = false 会执行第一段,设置params,此时最外层的宽高设置生效
// 第一段:设置temp的params,设置之后temp的最外层宽高才会生效
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
.... 省略了代码 ....
// 当root != null , attachToRoot = false不会执行第二段
// 第二段:是否需要把temp添加到root上
// root = null,所以此段代码不执行
// 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 != null , attachToRoot = false会执行第三段,将temp赋值给result并返回该result
// 第三段:result是否需要重新赋值
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
.... 省略了代码 ....
// 返回result对象
return result;
}
-
分析结果
能够看到,最后创建出来的View有设置params,所以被创建出来的布局,最外层设置的宽高是有效的,并最终返回了创建出来的View对象。
当root != null , attachToRoot = true
- 通过代码块+注释查看执行流程
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
.... 省略了代码 ....
// 1、声明返回的View对象 result,并赋予初始值为 root
View result = root;
// 2、createViewFromTag(root, name, inflaterContext, attrs)并不是本文重点,只需要关心这个temp对象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
/*
*下面的代码进行分段,每一段有每一段的逻辑处理
*/
// 当root != null , attachToRoot = true 会执行第一段,设置params,此时最外层的宽高设置生效
// 第一段:设置temp的params,设置之后temp的最外层宽高才会生效
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}
.... 省略了代码 ....
// 当root != null , attachToRoot = true会执行第二段,当前temp会被add到root上
// 第二段:是否需要把temp添加到root上
// root = null,所以此段代码不执行
// 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 != null , attachToRoot = true不会执行第三段,result是root
// 第三段:result是否需要重新赋值
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
.... 省略了代码 ....
// 返回result对象
return result;
}
-
分析结果
能够看到,最后创建出来的View有设置params,所以被创建出来的布局,最外层设置的宽高是有效的,并最终返回了root对象。
总结root对View的影响
- 当root为null时,被填充出来的 View对象最外层的宽高属性是无效的,
- 当root不为null时,被填充出来的View对象最外层的宽高属性是有效的,
总结attachToRoot对View的影响
- 当root为null时,被填充出来的View对象不会被添加到root上,且inflate()方法的返回值是被填充View本身,
- 当root不为null且attachToRoot为true时,被填充出来的View对象会被添加到root上,且inflate()方法的返回值是root,
- 当root不为null且attachToRoot为false时,被填充出来的View对象不会被添加到root上,且inflate()方法的返回值是被填充View本身。