LayoutInflater的inflate方法很常用,用于动态加载布局。inflate方法有四种调用方式:
实质上最后都是调用第4个方法,内部原理即用XmlPullParser解析xml文件,然后返回view对象。
接下来针对我们最常用的public View inflate(int resource, ViewGroup root, boolean attachToRoot)方法进行说明。
首先,参数说明。
1. resource:布局的资源id
2. root:填充的根视图
3. attachToRoot:是否将创建的师傅绑定到根视图中
那么问题来了?
inflate(layoutId, null )
inflate(layoutId, root, false)
inflate(layoutId, root, true)
以上三种方式到底有什么区别?接下来通过一个简单的示例来说明。
首页放置一个listview。下面是list_item.xml的内容。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="50dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/text_tv" android:textSize="16sp" android:text="Hello world!"/>
</LinearLayout>
main_activity.java的内容如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
ListView myListView = (ListView)findViewById(R.id.list_view);
myListView.setAdapter(new MyAdapter(this));
}
private class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
public MyAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 10;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(R.layout.list_item,null);
}
return convertView;
}
}
}
区别在于getView中的布局加载方式:
①convertView = inflater.inflate(R.layout.list_item,null);
②convertView = inflater.inflate(R.layout.list_item,parent,false);
③convertView = inflater.inflate(R.layout.list_item,parent,true);
三者的运行结果分别是这样的:
方式①如图。可见list_item根布局的属性没有生效。
方式②:
方式③:
程序crash。log如下:
接下来,回到inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) 方法中看看为什么会这样。
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
rInflate(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;
}
从源码中可以发现,如果root为null,因为layout_width,layout_height是相对父级视图设置的,必须与父级的LayoutParams一致。而此时的getLayoutParams为null,list_item根布局的属性会被忽略掉,而使用默认的wrap_content方式。所以设置的高度被忽略。如果要使高度生效,可以将根布局的layout_height改为wrap_content, 而将textview的layout_height改为50dp。如果root不为null,attachToRoot为false,则可以正确地处理。
如果root不为null,attachToRoot为true,则会将改布局通过addview绑定到root视图里去,而此时的root是我们的ListView,ListView为AdapterView的子类,listview不支持addview方法,因此方式③就报错了。但是如果换一种场景,假设根布局是LinearLayout,用方式③是正确的,相当于在原有布局中动态加载布局。
如果root不为null,attachToRoot为true则返回root;attachToRoot为false则返回加载的view本身。
看到这里,平时在使用inflate方法时,属性不生效、或者参数到底该怎么传也就明白了。