在Android开发中,最常用的控件就是ListView了,使用BaseAdapter创建自定义适配器时,覆写public View getView(int position, View convertView, ViewGroup parent)的时候,往往都是根据第一个参数position来获取对应item的数据,然后使用View.inflate()来创建一个View,试想一下,如果ListView有很多的Item,岂不是每一个Item都需要创建一个View,这样肯定是不够优雅的,优雅的实现就需要使用到ListView的缓存机制了
什么是ListView的缓存机制?
假设当前手机屏幕只能显示ListView中的7个Item,当ListView刚开始显示时,系统会调用7次适配器中的getView()来获取其对应的View显示在屏幕上,当ListView开始向上滑动的时候,会再次调用getView()来显示第8个Item,当第一个Item从屏幕上不可见时,第一个Item对应的View并不是被回收了,而是被放入了一个缓存区Recycle,当第8个Item要显示的时候,getView()的第二个参数convertView就会把缓存区中的View传递过来,供重复使用,如下图

LsitView缓存机制的验证
自定义适配器的布局创建请参见: BaseAdapter自定义适配器 这里只给出改变了的getView方法
public View getView(int position, View convertView, ViewGroup parent) {
ItemBean item = data.get(position);
boolean isNull = false;
if (convertView == null) {
isNull = true;
convertView = View.inflate(context, R.layout.lv_item, null);
}
ImageView img = (ImageView) convertView.findViewById(R.id.img);
TextView title = (TextView) convertView.findViewById(R.id.title);
TextView content = (TextView) convertView.findViewById(R.id.content);
img.setImageResource(item.getItemImg());
title.setText(item.getItemTitle());
content.setText(item.getItemContent());
Log.v("client","position = " + position + ":view" + convertView.hashCode()+ "lsNull:" + isNull);
return convertView;
}
Log日志(屏幕上可见0-8的item)

当ListView初次显示的时候,缓冲区为空,getView()的第二个参数为null,当,第9项显示,第一项未完全不可见的时候,缓冲区依然为null,当第一项不可见以后,可以看到,position=10的View.hashCode()与第一项相同,同时缓冲区不为null,这样,不论有多少项,在内存中只需要屏幕可见项+1数量的View即可
自定义适配器的优化一
逗比式做法就是每一次都新建一个View,普通式,就是每一次判断第二个参数convertView是否为空,也就是缓冲区是否存在View,没有则创建,有则直接使用
public View getView(int position, View convertView, ViewGroup parent) {
ItemBean item = data.get(position);
if (convertView == null) {
convertView = View.inflate(context, R.layout.lv_item, null);
}
ImageView img = (ImageView) convertView.findViewById(R.id.img);
TextView title = (TextView) convertView.findViewById(R.id.title);
TextView content = (TextView) convertView.findViewById(R.id.content);
img.setImageResource(item.getItemImg());
title.setText(item.getItemTitle());
content.setText(item.getItemContent());
return convertView;
}
自定义适配器优化二
优化一中,每一次都需要从convertView中使用findViewById()方法去获取控件,当布局文件很复杂时,这无疑也是加大了时间成本,还有一种方法是用空间换取时间,定义一个内部类ViewHolder对应每一个Item的控件,然后通过convertView的的setTag()方法保存,使用的时候通过getTag()直接获取ViewHolder,这样除了第一次需要findViewById()以外,以后都可以不用了,这也是谷歌推荐的做法
public class MyAdapter extends BaseAdapter {
private Context context;
private List<ItemBean> data;
public MyAdapter(Context context, List<ItemBean> data) {
this.context = context;
this.data = data;
}
// 获取listview的总数
@Override
public int getCount() {
return data.size();
}
// 获取与指定索引相对应的数据集
@Override
public Object getItem(int position) {
return data.get(position);
}
// 获取listview对应项的id
@Override
public long getItemId(int position) {
return position;
}
// 获取item显示的内容
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemBean item = data.get(position);
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(context, R.layout.lv_item, null);
ImageView img = (ImageView) convertView.findViewById(R.id.img);
TextView title = (TextView) convertView.findViewById(R.id.title);
TextView content = (TextView) convertView.findViewById(R.id.content);
//创建一个ViewHolder对象,用于保存findViewById的结果
holder = new ViewHolder(img, title, content);
convertView.setTag(holder);
}
//获取存储的结果,浪费了空间,节约了时间
holder = (ViewHolder) convertView.getTag();
holder.getImg().setImageResource(item.getItemImg());
holder.getTitle().setText(item.getItemTitle());
holder.getContent().setText(item.getItemContent());
return convertView;
}
class ViewHolder {
private ImageView img;
private TextView title;
private TextView content;
public ViewHolder(ImageView img, TextView title, TextView content) {
this.img = img;
this.title = title;
this.content = content;
}
public ImageView getImg() {
return img;
}
public void setImg(ImageView img) {
this.img = img;
}
public TextView getTitle() {
return title;
}
public void setTitle(TextView title) {
this.title = title;
}
public TextView getContent() {
return content;
}
public void setContent(TextView content) {
this.content = content;
}
}
}
源码下载:360云盘 访问密码 fe60