在开发过程中,我们通常都有在用View.inflate(context, resource, root)方法来创建视图,这个方法非常方便,但是它有个缺点就是没有加载xml 里面设置的布局参数。举个例子创建一个叫ListViewDemo 的工程。它里面主界面布局里面就放置一个listview
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
在activity 中就是获取到listview 控件并给它设置一个适配器
MainActivity .java
package com.itheima.listviewdemo;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(new MyAdapater(this));
}
private class MyAdapater extends BaseAdapter {
private LayoutInflater mInflater;
public MyAdapater(Context mcontext) {
mInflater = (LayoutInflater) mcontext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return 10;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = mInflater.inflate(R.layout.item, parent, false);
// View view = mInflater.inflate(R.layout.item, null);
return view;
}
}
}
listview 的item 布局为item.xml,它是一个宽度为match_parent,高度为100dp 的线性
布局,线性布局包含了一个宽度为match_parent、高度为match_parent 的TextView。
【文件1-3】item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical" >
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00"
android:text="黑马程序员"/>
</LinearLayout>
从布局上来看,listview 的条目textview 控件的高度应该为100dp。但是我们在适配器方法getview 中使用mInflater.inflate(R.layout.item, null)方法后(见【文件1-2】54 行),运行的效果如图1-1(a)所示,它的高
度为包裹内容,这说明它没有使用到xml 中设置的属性高度。
ListViewDemo 运行效果
如果使用mInflater.inflate(R.layout.item, parent, false),运行效果如图1-1(b)所示,它的高度为100dp,
这样xml 里设置的高度就生效了,这才是inflate 方法的正确用法。
那我们来看看它为什么可以正确显示xml 布局中的数据。我按住ctrl+左键进入mInflater.inflate
(R.layout.item, parent, false) 方法的源码,方法中通过第一个参数布局资源id 获取到一个XmlResourceParser
对象,接着调用inflate(parser, root, attachToRoot)方法(见【文件1-4】3~5 行)。在该方法中首先通过final
AttributeSet attrs = Xml.asAttributeSet(parser) 这段代码拿到xml 中的属性值; 再通过temp =
createViewFromTag(root, name, attrs) 这段代码得到根据属性值attrs 对象创建出来的view 。
createViewFromTag(root, name, attrs)方法中通过调用createView 方法使用反射的方式创建出控件对象;在
inflate(parser, root, attachToRoot)方法中还有一步重要的操作:temp.setLayoutParams(params),这里temp 的
是上面createViewFromTag 方法根据xml 创建出来的view,给它设置了xml 里设置的布局参数,这样xml
里设置的高度就生效了。(这里源码过多就不贴出来了)
源码执行顺序:inflate(R.layout.item, parent,false)→XmlResourceParser →inflate(parser, root,
attachToRoot)→AttributeSet attrs = Xml.asAttributeSet(parser)→temp = createViewFromTag(root, name, attrs)→
→temp.setLayoutParams(params),这是inflate 方法的执行顺序。其中createViewFromTag(root, name, attrs)
方法中通过view = createView(name, null, attrs)方法使用反射来创建xml 中的控件,这样一个xml 转换为
view 的步骤原理就介绍完了。
【文件1-4】Inflate 源码
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}