ListView的缓存机制

/ 0评 / 0

在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传递过来,供重复使用,如下图

listView02

 

 

 

 

 

 

 

 

 

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)

listView04

当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

发表评论

您的电子邮箱地址不会被公开。