LayoutInflater.inflate()解析
布局加载器加载视图的方法有以下几种:
- public View inflate(int resource, ViewGroup root)
- public View inflate(int resource, ViewGroup root, boolean attachToRoot)
- public View inflate(XmlPullParser parser, ViewGroup root)
- public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
resource会通过Pull解析器被解析成XmlPullParser类型的parser, 故只需要看第四个方法就知道是怎么回事了!一般我们使用它的时候有三种使用方法:
- inflate(parser, null, false);
- inflate(parser, root, false);
- inflate(parser, root, true);
//在API25源码复制
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
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 {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// 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 (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// 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);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
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 {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
- inflate(parser, null, false);
- inflate(parser, root, false);
- inflate(parser, root, true);
第一种情况:
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
temp是啥?往前看:
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
temp is the root view that was found in the xml.源码中的注释写的很清晰。temp就是你加载的那个xml文件的根视图。也就是说第二个参数root如果是空就直接返回xml文件里的根视图。接下来看看root!=null的情况:
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// 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);
}
...
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
现在更清晰了,当我们传进来的root不是null,并且第三个参数是false的时候,这个temp会使用root生成的layoutParams,并作为返回值返回了。而当我们设置root为空的时候,没有设置LayoutParams参数的temp对象,作为返回值返回了。这时候我们发现只要是root为null或者attachToRoot为false都会将root返回,那么inflate(parser, null, false)和inflate(parser, root, false)又有什么区别呢?实际上这个root是作为一个协助生成view的容器,如果我想让生成的布局的根节点有效,又不想让它处于某一个容器中,那我就可以设置root!=null,而attachToRoot为false。由此可见root会协助第一个参数所指定布局的根节点生成布局参数!
再回头看代码,当root!=null,attachToRoot为true时:
// 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);
}
很清楚,当attachToRoot为true时,会将第一个参数所指定的布局添加到第二个参数的View中。
总结一下:当root==null时,直接返回要加载布局文件的根视图;当root!=null时,如果attachToRoot==false,系统会通过将root作为一个容器,协助生成view;如果attachToRoot==true,会将生成的view添加到root中。