android ListView优化之ViewHolder的超简洁写法

一、常规的ViewHolder写法

在android开发中,ListView是经常使用到的一个控件,而为ListView写适配器代码时,我们总是继承BaseAdapter之后,复写其中的getView()方法,在getView方法中创建一个包含了list item中所有控件的ViewHolder类,判断当convertView为空时,就new一个ViewHolder类,然后用findViewById将list item中的控件一个个找到,这种写法的代码如下:

public class PostListAdapter extends BaseAdapter {
	private Context context;
	private List<PostBean> data;
	
	public PostListAdapter(Context context, List<PostBean> data){
		this.context = context;
		this.data = data;
	}

	public int getCount() {
		return data.size();
	}

	public Object getItem(int position) {
		return null;
	}

	public long getItemId(int position) {
		return position;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if(convertView == null){
			convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);
			holder = new ViewHolder();
			holder.nick = (TextView) convertView.findViewById(R.id.item_nick);
			holder.time = (TextView) convertView.findViewById(R.id.item_time);
			holder.title = (TextView) convertView.findViewById(R.id.item_title);
			convertView.setTag(holder);
		}else{
			holder = (ViewHolder) convertView.getTag();
		}
		PostBean bean = data.get(position);
		holder.nick.setText(bean.getNick());
		holder.title.setText(bean.getTitle());
		holder.time.setText(bean.getTime());
		return convertView;
	}
	
	class ViewHolder {
		TextView nick;
		TextView title;
		TextView time;
	}

}

二、更加简洁且通用的ViewHolder写法

上面这种写法虽然优化了ListView的性能,但是代码太复杂了,针对不同的list item,我们要创建不同的内部类ViewHolder,然后一个个findViewById,这里有一种更简洁的ViewHolder写法,只需要一个静态方法,就可以适用于所有的ListView,先上代码:

public class ViewHolder {

	@SuppressWarnings("unchecked")
	public static <T extends View> T get(View view, int id){
		SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
		if(viewHolder == null){
			viewHolder = new SparseArray<View>();
			view.setTag(viewHolder);
		}
		View childView = viewHolder.get(id);
		if(childView == null){
			childView = view.findViewById(id);
			viewHolder.put(id, childView);
		}
		return (T) childView;
	}
}
上面的代码创建了一个ViewHolder类,在类中提供了一个静态方法get(),方法的参数有两个,第一个是list item的layout对应的View,即适配器类中getView()方法中的convertView,第二个参数为整型的id,即list item中某个控件的id值,下面是ViewHolder类的用法,看代码:

public class MyListViewAdapter extends BaseAdapter {
	private Context context;
	private String[] titles = new String[20];
	private String[] subtitles = new String[20];
	
	public MyListViewAdapter(Context context){
		this.context = context;
		for(int i = 0; i < 20; i++){
			titles[i] = new String("这是标题" + i);
			subtitles[i] = new String("子标题" + i);
		}
	}

	public int getCount() {
		return titles.length;
	}

	public Object getItem(int position) {
		return null;
	}

	public long getItemId(int position) {
		return 0;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView == null){
			convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);
		}
		ImageView img = ViewHolder.get(convertView, R.id.item_img);
		TextView title = ViewHolder.get(convertView, R.id.item_title);
		TextView subtitle = ViewHolder.get(convertView, R.id.item_sub_title);
		img.setImageResource(R.drawable.ic_launcher);
		title.setText(titles[position]);
		subtitle.setText(subtitles[position]);
		return convertView;
	}

}
ViewHolder类的使用主要在上面的getView()方法中,我们在获取list item中的控件时,只需要调用ViewHolder.get()方法,传入convertView和某个控件的id,就可以方便地获取到该控件,是不是很牛叉,代码太简洁了~

三、学习ViewHolder中get()方法的思路

ViewHolder中的get()方法里,使用了一个类:SparseArray,该类是android.util包中提供的一个类,主要用于代替Map<Integer, Object>,SparseArray内部通过键值对存放数据,其中键是整型数据,SparseArray的内部采用折半查找的思想,和普通的Map<Integer, Object>相比,拥有更好的性能,下面分析一下ViewHolder.get()方法:

public class ViewHolder {

	@SuppressWarnings("unchecked")
	public static <T extends View> T get(View view, int id){
		//从convertView中获取tag,SparseArray里面存放View和View对应的id,通过View的id来查找View
		SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
		//如果获取不到,则创建SparseArray对象,并将SparseArray对象放入convertView的tag中
		if(viewHolder == null){
			viewHolder = new SparseArray<View>();
			view.setTag(viewHolder);
		}
		//通过View的id来获取View,在SparseArray的内部使用的折半查找
		View childView = viewHolder.get(id);
		//如果在SparseArray中没有对应的id,则通过findViewById来获取View
		if(childView == null){
			childView = view.findViewById(id);
			//将获取到的View和与View对应的id存入SparseArray中
			viewHolder.put(id, childView);
		}
		//返回值是方法的第二个参数id对应的View
		return (T) childView;
	}
}

上面这种ViewHolder写法是不是碉堡了,在我们的项目中,以后只需要写这么一个ViewHolder类,加上get()这个静态方法,就可以通用于所有的ListView了,不用像常规写法那样,为每一个ListView的Adapter都创建一个ViewHolder内部类,然后findViewById。下面放一个demo代码, 点击这里下载

效果图如下:

android ListView优化之ViewHolder的超简洁写法_第1张图片



你可能感兴趣的:(android,优化,ListView,Adapter,viewholder)