<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android
"
android:layout_width="match_parent"
android:layout_height="100dip"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />
LinearLayout>
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflate(R.layout.item_lv_test, null);
}
return convertView;
}
如果用上面的代码会发现设置100dp是无效的。而如果换成下面的代码就可以了。
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflate(R.layout.item_lv_test, null, false);
}
return convertView;
}
在Android开发中为了inflate一个布局文件,大体有2种方式,如下所示:
// 1. get a instance of LayoutInflater, then do whatever you want
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 2. you're in some View class, then just call View's static inflate method
View.inflate(context, R.layout.xxx_xml, someViewGroup/null);
我们来看看这2种方式的具体源码:
public static View inflate(Context context, int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
1)现在我们看到实质上都是方法1中的做法,View.inflate只是个helper方法而已(少敲几行代码)。那么我们就先来看看LayoutInflater类。关键代码如下:
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
1)实际上调用3个参数的版本,从这里我们可以看出客户端代码
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
说的更明白一点就是对 ViewGroup root, boolean attachToRoot这两个参数的讨论。传入ViewGroup root, boolean attachToRoot的值不同,会出现什么结果呢?
先初略解释下这两个参数:
ViewGroup root:指实例的布局所要放入的根视图。
boolean attachToRoot:指是否附加到传入的根视图。
先分析源码:
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 {
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 (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(" 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);
}
}
// 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.
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;
}
}
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
1)此方法最后的返回值,初始化为传入的root
View result = root;
2)能走到这里,说明type是START_TAG 或 END_DOCUMENT,如果一开始就是END_DOCUMENT,那说明xml文件有问题.
if (type != XmlPullParser.START_TAG)
3) 获得当前start tag的name,能到这里,那type一定是START_TAG,也就是xml文件里的root
final String name = parser.getName();
4)判断当前的tag是否是merge标签,root必须非空且attachToRoot为true,否则抛异常结束。
if (TAG_MERGE.equals(name))
5) 递归的inflate
rInflate(parser, root, inflaterContext, attrs, false);
6)动态载入resource
final View temp = createViewFromTag(root, name, attrs, false)
7)创建ViewGroup的LayoutParams
ViewGroup.LayoutParams params = null;
params = root.generateLayoutParams(attrs);
8)当root不为空,attachToRoot为false时,为temp设置layout属性,当该view以后被添加到父view当中时,这些layout属性会自动生效
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
9)递归inflate剩下的所有children
rInflateChildren(parser, temp, attrs, true);
至此我们已经将LayoutInflater.inflate的关键代码分析完毕了。