一、常规的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代码, 点击这里下载
效果图如下: