在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