需求
当界面上需要有多个条目需要显示的时候,而且条目数量不定,在xml文件中定义控件就无能为力了,这种情况下可以使用ListView,用来显示多个结构相同的条目,比如新闻客户端,应用市场app列表中,每个条目都是由相同的结构构成的,条目总数目来自于数据库,编程时未知。
步骤
首先定义一个Listview控件,找到它,设置它的适配器。
1. 定义控件
在布局文件中定义一个ListView控件,加上id方便获取,给予适当的宽高。
2. 设置Adapter
这里常用的Adapter有三种,BaseAdapter,SimpleAdapter,ArrayAdapter。其中:
baseAdapter是一个抽象类,需要继承并实现抽象方法才能使用,封装较浅,最为灵活
SimpleAdapter是BaseAdapter的子类,能通过自定义布局作为每个条目的结构来显示。
ArrayAdapter也是BaseAdapter的子类,封装度很高,能够很简单的使用,但是只能用来显示单项文本。
使用 BaseAdapter
写一个Adapter继承BaseAdapter,实现四个方法
class MyAdapter extends BaseAdapter {
//有系统调用,用来获知模型层有多少条数据
@Override
public int getCount() {
return plist.size();
}
@Override
public Object getItem(int position) {
return plist.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
//由系统调用 返回的view对象就会作为listView的一个条目显示在屏幕上
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
//convertView就是已经缓存好的View对象,应尽量使用已经缓存的对象,节约内存
if (convertView == null) {
//获取条目的布局文件,将布局文件填充成view对象
view = View.inflate(MainActivity.this, R.layout.personlayout, null);
} else {
view = convertView;
}
//获取布局文件里面的控件
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
<pre name="code" class="java"> //设置文本
Person person = plist.get(position); tv_name.setText(person.getName()); tv_phone.setText(person.getPhone()); tv_salary.setText(person.getSalary() + "");//如果参数是字符串,则设置字符串为文本值,如果参数是整数,则以整数为id去查找相应的资源 return view; } }
使用viewHolder;
还有一个性能优化的空间就是,每次从id中找控件,每次都是相同的id,得到相同的控件。使用viewholeder可以减少findviewbyId的次数,可以提高运行速度。
定义一个类,将控件作为其成员存在,在第一次调用getview的时候,实例化这个类,将所有findviewbyid得到的控件存入该实例,再将实例作为一个Tag保存在View中,这样,之后调用的都是缓存后的ConvertView,它的Tag肯定就是这个实例了,然后直接就可以使用这个实例的成员作为控件进行设置了。
public View getView(int position, View convertView, ViewGroup parent) {
News news = newses.get(position);
View view;
MyViewHolder mhoder;
if (convertView == null) {
mhoder = new MyViewHolder();
view = View.inflate(MainActivity.this, R.layout.newslayout, null);
mhoder.tv_title = (TextView) view.findViewById(R.id.tv_title);
mhoder.tv_detail = (TextView) view.findViewById(R.id.tv_detail);
mhoder.tv_comment = (TextView) view.findViewById(R.id.tv_comment);
mhoder.imageView = (SmartImageView) view.findViewById(R.id.imageview);
view.setTag(mhoder);
} else {
view = convertView;
mhoder = (MyViewHolder) view.getTag();//这样的话,不管convertview是否为空,都能保证mholder是有值的。
}
mhoder.imageView.setImageUrl(news.getImagePath());
mhoder.tv_title.setText(news.getTitle());
mhoder.tv_detail.setText(news.getDetail());
mhoder.tv_comment.setText(news.getComment());
return view;
}
class MyViewHolder{
private TextView tv_title;
private TextView tv_detail;
private TextView tv_comment;
private SmartImageView imageView;
}
设置Adapter
if (lv != null) lv.setAdapter(new MyAdapter());
结果
使用SimpleAdapter
SimpleAdapter不是抽象类,直接new就可以使用
if (lv != null)
lv.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.personlayout,
new String[]{"name","phone","salary"},//data中map的key
new int[]{R.id.tv_name, R.id.tv_phone, R.id.tv_salary}));//将map中的数据放到哪里去
构造函数参数: arg0 Context,arg1 List<Map<String,Object>>, arg2 int, arg3 String[], arg4 int[]
其中 arg1 (data) 是一个List,泛型是Map,map的泛型是<String,?>,每一个map可以理解为一个一个条目的数据,整个list就是所有的条目的数据,而map中的每一个键值对就是每一个条目中的一个部分的标志和值。
其中arg2是描述每一个条目的布局结构的布局文件的资源id。
arg3 是一个字符串数组,与Map<String,?>中的key String。
arg4 是一个int数组,描述的是将Map中Key所对应的value设置给哪些控件,如果对应的是ImageView,那么arg3中的value应该是图片的资源Id
如:
for (Person person:plist
) {
Map<String, Object> map = new HashMap<>();
map.put("name", person.getName());
map.put("phone", person.getPhone());
map.put("salary", person.getSalary());
data.add(map);
}
因为这里不过就是将BaseAdapter做了封装,使用的布局和数据都是一样的,所以运行结果和BaseAdapter是一样的
使用ArrayAdapter
ArrayAdapter封装度极高,只能将字符串数组作为data
if (lv!=null) lv.setAdapter(new ArrayAdapter<String>(MainActivity.this,R.layout.personlayout,R.id.tv_name,new String[]{"zhangsan","lisi"}))
可以看到定义的条目布局中只能有一个根据数据变动,应为它只能接收一个字符串数组作为data,但是使用起来也非常简单。
总结
Listview可以用来显示多条目的数据,作用就相当于JavaWeb MVC框架中的jsp。Adapter就相当于Servlet(Controller),是控制显示的。
三种Adapter各有不同,使用BaseAdapter的话,各种方法需要自己实现,如使用ConvertView来节约内存(不用的话后果很严重)等。而其他两个都已经实现好了,直接用就可以。最简单的实现就要用ArrayAdapter,需要显示特定结构的条目就需要使用SimpleAdapter,如果都不能满足,才应该考虑使用BaseAdapter。