android LayoutInflater
android开发了几年,对于布局填充器(LayoutInfalter)这个类的功能并不会陌生,但是从来没有看过起源码,常常会因为parent,attachToRoot这几个参数的传法而出问题,今天正好碰上又传错了参数,所以抽时间捋一下源码,和各个参数的使用方法。
android中将一个xml转换成view我们一般会使用
View.inflate和LayoutInflater.inflate这两个方法,但是这两个方法究竟有什么区别, 基于android-29 源码进行分析,不想看源码的可以直接查看结论。
一、View.inflate
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
可以发现View.inflate最终调用的是LayoutInflater.inflate方法,所以最终弄懂LayoutInflater.inflate即可
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
//最终调用的是LayoutInflater中的一个infalte重载方法
//当root 传入有值的时候,attachToRoot就是true
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
所以最核心的地方归结到inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)这个方法中来。
View.inflate方法其实就是对LayoutInfalter中的方法的包装,方便开发者使用。
二、LayoutInflater.inflate
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
... 忽略 后续版本添加的提升性能方法 ...
//Resource 获取到Xml的解析器
XmlResourceParser parser = res.getLayout(resource);
try {
// 调用LayoutInflater的infate重载方法
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
// 同步方法
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
//解析出xml中各个属性集合
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
//默认返回的就是传入的root 如果出现任何的Exception
View result = root;
try {
//校验是否是闭合的节点
advanceToRootNode(parser);
// 获取根节点的名字
final String name = parser.getName();
...省略中间调试代码...
if (TAG_MERGE.equals(name)) {
...省略对merge标签的处理...
} else {
//对普通view标签的处理
// Temp is the root view that was found in the xml
//生成传入xml文件的根view
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// 生成传入的根目录对应的布局参数
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
//如果不添加到传入的parent布局 则直接设置这个layoutparams 这个时候其实就是 warp_content 因为没有父布局的信息
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
//解析xml跟布局中的子布局
rInflateChildren(parser, temp, attrs, true);
//如果传入的parent不为空 切xml需要贴到parent的view中
//这个时候子view 没有布局参数 参数都是warp
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//如果传入的root为空 或者不用贴到布局之上 直接返回xml布局view
if (root == null || !attachToRoot) {
result = temp;
}
}
}
...异常处理...
return result;
}
}
最终都会调用到public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 这个方法中来,返回最终xml对用的view,但是传入的参数不同,造成的结果也会存在差异
三、结论
-
使用LayoutInflater这个类比使用View的静态方法更灵活,推荐直接使用
LayoutInflater
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
- 对于在adapter中使用的时候 传 root attachToRoot = false
- attachToRoot = true root != null 可以获取到root的布局参数
作为个人笔记使用,有问题一起探讨!勿喷