浅析Android中的inflate

部分转载自这里

LayoutInflater.from(context).inflate(layout, root,attachToRoot);

LayoutInflater.inflate一共有很多个重载。最终都会调用到以下这个方法。

1
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

先看例子
textview_layout

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Text Test"
android:background="@color/colorAccent"/>

textview_layout_parent

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?> <LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"> <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Text Test"
android:background="@color/colorAccent"/> </LinearLayout>

activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class InflateAdapter extends BaseAdapter {
private LayoutInflater mInflater = null; public InflateAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 8;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//说明:这里是测试inflate方法参数代码,不再考虑性能优化等TAG处理
return getXmlToView(convertView, position, parent);
}
private View getXmlToView(View convertView, int position, ViewGroup parent) {
View[] viewList = {
mInflater.inflate(R.layout.textview_layout, null),
mInflater.inflate(R.layout.textview_layout, parent, false),
//mInflater.inflate(R.layout.textview_layout, parent, true),
mInflater.inflate(R.layout.textview_layout, null, true),
mInflater.inflate(R.layout.textview_layout, null, false), mInflater.inflate(R.layout.textview_layout_parent, null),
mInflater.inflate(R.layout.textview_layout_parent, parent, false),
//mInflater.inflate(R.layout.textview_layout_parent, parent, true),
mInflater.inflate(R.layout.textview_layout_parent, null, true),
mInflater.inflate(R.layout.textview_layout_parent, null, false),
}; //会抛出Caused by: java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView
convertView = viewList[position]; return convertView;
}

最终运行

20180530152765088467470.png

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
//这里是丛XML解析属性
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();
//这里判断的是Merge标签。用Merge的时候根布局不能为null且attachToRoot不能为false
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);
//这里可以看出只有root不为null且attachToRoot为false的时候,temp才会设置它自己的LayoutParams
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
//这里可以看出root不为null且attachToRoot为true的时候执行了一次addView
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;
}
}
//最终会把view返回
return result;
}
}

从上面的源码分析我们可以看出inflate方法的参数含义:

  • inflate(xmlId, null); 只创建temp的View,然后直接返回temp。

  • inflate(xmlId, parent); 创建temp的View,然后执行root.addView(temp, params);最后返回root。

  • inflate(xmlId, parent, false); 创建temp的View,然后执行temp.setLayoutParams(params);然后再返回temp。

  • inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp, params);最后返回root。

  • inflate(xmlId, null, false); 只创建temp的View,然后直接返回temp。

  • inflate(xmlId, null, true); 只创建temp的View,然后直接返回temp。

  • mInflater.inflate(R.layout.textview_layout, null)不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null。

  • mInflater.inflate(R.layout.textview_layout, parent)能正确显示我们设置的宽高是因为我们的View在设置setLayoutParams时params = root.generateLayoutParams(attrs)不为空。
    Inflate(resId , parent,false ) 可以正确处理,因为temp.setLayoutParams(params);这个params正是root.generateLayoutParams(attrs);得到的。
  • mInflater.inflate(R.layout.textview_layout, null, true)mInflater.inflate(R.layout.textview_layout, null, false)不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null。
  • textview_layout_parent.xml作为item可以正确显示的原因是因为TextView具备上级ViewGroup,上级ViewGroup的layout_width,layout_height会失效,当前的TextView会有效而已。

    inflate(xmlId, parent)inflate(xmlId, parent,true)是会直接崩溃的。原因是AdapterView源码中调用了root.addView(temp, params);而此时的root是我们的ListView,ListView为AdapterView的子类,所以我们看下AdapterView抽象类中addView源码即可明白为啥了,如下:

1
2
3
4
5
6
7
8
/**
* This method is not supported and throws an UnsupportedOperationException when called. * * @param child Ignored.
* * @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void addView(View child) {
throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
}