其实View.inflate(context, layoutResId, root)
是对LayoutInflater.from(context).inflate(layoutResId, root, attachToRoot)
的封装
最终会调用下面的方法,这将根据不同的传入参数得到不同的结果
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
View result = root;
...
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);
}
}
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
result = temp;
}
}
return result;
}
}
复制代码
当调用View.inflate(context, layoutResId, root)
时会调用LayoutInflater.inflate(layoutResId, root)
然后调用LayoutInflater.inflate(layoutResId, root, root != null)
。
即调用View.inflate(context, layoutResId, root)
时root == null
实际等价于LayoutInflater.from(context).inflate(layoutResId, null, false)
,而root != null
时等价于LayoutInflater.from(context).inflate(layoutResId, root, true)
,所以说讨论View.inflate(context, layoutResId, root)
与LayoutInflater.from(context).inflate(layoutResId, root, attachToRoot)
的异同实际上就是讨论不同root
与attachToRoot
传入参数情况下的影响。
1 在直接代码上表现的差异
1.1 root == null
,attachToRoot == false
(其实只要root
为null
,attachToRoot
取何值都一样,这里取false
)
当调用View.inflate(context, layoutResId, null)
时即LayoutInflater.from(context).inflate(layoutResId, null, false)
,将执行文首贴出的inflate
方法,执行相应逻辑
View result = root;
if (root == null || !attachToRoot) {
result = temp;
}
return result;
复制代码
return的是view他自己,view是解析layoutResId布局生成的View。
1.2 root != null
,attachToRoot == true
当调用View.inflate(context, layoutResId, null)
时即LayoutInflater.from(context).inflate(layoutResId, root, true)
,将执行文首贴出的inflate
方法,执行相应逻辑
View result = root;
if (root != null) {
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
}
if (root != null && attachToRoot) {
root.addView(temp, params);
}
return result;
复制代码
给view设置解析得到的LayoutParams(含layoutResId对应xml中声明的宽高信息)并添加到root,return的是view的父布局root。
1.3 root != null
,attachToRoot == false
只能直接调用LayoutInflater.from(context).inflate(layoutResId, root, false)
,View中的inflater静态方法无法做到,在文首贴出的inflate
方法中执行的逻辑是
View result = root;
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);
}
}
return result;
复制代码
给view设置解析得到的LayoutParams(含layoutResId对应xml中声明的宽高信息),return的是view他自己。
2 流程和控制上的差异
由此可以看出的root
和attachToRoot
参数的差异将决定view是否将会添加到root,是否会给View设置解析得到的LayoutParams,甚至是返回值。
但是这里影响最大的是是否给view设置解析好的LayoutParams,也是我们最关注的差异。为什么?
经过前面的分析知道,return分为view他自身和view的父布局root两种情况,我们在做引用的时候肯定要明确什么时候返回什么。
其实这对View.inflate
和LayoutInflater.inflate
而言并没有差别,这两种情况他们都可能返回这两种值。
view是否添加到root是由attachToRoot
参数控制影响的,这是开发者自行传入控制的参数。开发者明确知道想要控制view被添加到容器的时机、对象,添加的时机就是inflate的时候(attachToRoot == true
)或者其他任何时候(attachToRoot == false
)
问题来了,传入attachToRoot == true
,inflater的时候把就view添加到Root包含解析得到LayoutParams信息,符合我们的预期(我们在布局xml中声明的layout_width
、layout_height
宽高值)。而传入attachToRoot == false
,root也为null,在view中没有解析的LayoutParams信息,等我们自己addView时会给一个默认的LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
,覆盖在布局xml声明的宽高值,导致我们的设置失效。
总结
到现在我们就知道,为什么一些有经验的人会说不要用View.inflate
而是用LayoutInflater.inflate
,主要是View.inflate(context, layoutResId, null)
会让我们在layoutResId布局中声明的宽高信息无效(MATCH_PARENT或具体宽高),而用LayoutInflater.from(context).inflate(layoutResId, root, false)
替代,后者没有这个问题。