在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; } }
上面这种写法虽然优化了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,就可以方便地获取到该控件,是不是很牛叉,代码太简洁了~
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; } }
效果图如下: