这两天看到 LayoutInflater 的分析文章,有点问题,于是自己看了一下源码,动手试了试验证一下。正所谓纸上得来终觉浅。
源码简单说明
Layoutinflater 生成 View 有4个重载的方法:
public View inflate(int resource, ViewGroup root)
public View inflate(XmlPullParser parser, ViewGroup root)
public View inflate(int resource, ViewGroup root, boolean attachToRoot)
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
前面 3 个方法最终都会调用第 4 个方法,重载的惯用伎俩,呵呵。所以看第4个是什么样的。
源码:呃,看起来有点多,但其实关键的就几句,其他都是一些 debug 日志什么的。
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;
}
}
整个流程下来大概就是:使用XmlPullParser 解析 layout 文件,然后根据标签名使用 createViewFromTag
方法创建 View 实例。使用 rInflateChildren(parser, temp, attrs, true)
递归创建子View。接下来就是根据 root 和 attachToRoot 参数的情况,看是否要创建 LayoutParams,是否调用 root 的 addView 方法将 View 放到 root 里面去。多说一句,这里的 root 就是我们从 inflater 传递进来的ViewGroup 参数。 最后如果 attachToRoot 是 true 的话,返回 root,否则返回新创建的 View。
整个看下来:关键就在于参数 root 和 attachToRoot
- root 等于 null,不生成 LayoutParams 不添加到 root
- root 不等于 null,生成 LayoutParams。这时候如果 attachToRoot 是 fasle,表示不添加到 root,将LayoutParams 设置给生成的 View,并将 View 返回。如果attachToRoot 是 true,则调用
root.addView(temp, params);
将 View 添加到 root,并将 root 返回。
第一种情况,返回的 View 没有设置 LayoutParams,第二种情况则有。接下来试一下
代码测试
代码:root 等于 null
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.button, null);
// layout.addView(view);
ViewGroup.LayoutParams params = view.getLayoutParams();
Log.d("debug", "null: " + (params == null));
运行结果:

代码:root 不等于 null,attachToRoot = false
LinearLayout layout = (LinearLayout) findViewById(R.id.m_content);
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.button, layout, false);
// layout.addView(view);
ViewGroup.LayoutParams params = view.getLayoutParams();
Log.d("debug", "null: " + (params == null));
//打印LayoutParams log
logParams(params);
运行结果:

代码:root 不等于 null,attachToRoot = true
LayoutInflater.from(this).inflate(R.layout.button, layout, true);
// layout.addView(view);
Button view = (Button) layout.getChildAt(0);
ViewGroup.LayoutParams params = view.getLayoutParams();
Log.d("debug", "null: " + (params == null));
//打印LayoutParams log
logParams(params);
运行结果:

从上图可以看出,Button 已经被添加到 LinearLayout 里去了
另外的重载方法
源码:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
可以看出:不设置 attacheToRoot ,值默认为 root != null
所以。。。
其他
View 类也有个 inflater 方法,内部也是用 LayoutInflater 实现的。
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
参考文章:
1.Android LayoutInflater原理分析,带你一步步深入了解View(一)
2.2
网友评论