一、前言
项目中经常用到ListView、GridView。因此写了很多 Adapter。发现其中的代码非常类似。仅有的区别在于 viewHolder 的创建、将数据绑定到 viewHolder。所以就想能不能偷个懒。
二、思路
1、把不变的代码放到一个类里面,起名 CommonBaseAdapter。(它的构造器以自定义的 CommonViewHolder 的类(.class)为参数。在 getView 函数中,利用反射,实例化一个 viewHolder。之后,利用多态,绑定数据。)
2、把可变的代码抽象一下,创建一个虚基类,起名 CommonViewHolder。
3、需要一个新的 Adapter 的时候,继承CommonViewHolder,重写里面的函数。(这里要写的代码的全部是自己的业务逻辑。比如,在构造器里调用 convertView.findViewById 初始化自己的域、在 setItem 里绑定数据或者设置监听器等等。)
4、以自定义的 CommonViewHolder 的类(.class)为参数创建出可用于ListView、GridView 的 adapter。
三、源码
/**
* @param <T>
*/
public class CommonBaseAdapter<T> extends BaseAdapter {
protected static final String TAG = CommonBaseAdapter.class.getSimpleName();;
private Context context;
private int layout;
private Class<? extends CommonViewHolder<T>> viewHolderClass;
private List<T> datas;
/**
* @param context
* @param layout
* @param datas
* @param viewHolderClass
*/
public CommonBaseAdapter(Context context, int layout, List<T> datas, Class<? extends CommonViewHolder<T>> viewHolderClass) {
super();
this.context = context;
this.layout = layout;
this.datas = datas;
this.viewHolderClass = viewHolderClass;
}
/**
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
CommonViewHolder<T> holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(layout, parent, false);
try {
holder = viewHolderClass.getDeclaredConstructor(View.class).newInstance(convertView);
} catch (NoSuchMethodException e){
e.printStackTrace();
Log.e(TAG, e.toString());
} catch (SecurityException e){
e.printStackTrace();
Log.e(TAG, e.toString());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, e.toString());
}
convertView.setTag(holder);
} else {
holder = (CommonViewHolder<T>) convertView.getTag();
}
holder.setItem(datas.get(position));
return convertView;
}
/**
* @return
*/
@Override
public int getCount() {
return datas.size();
}
/**
* @param position
* @return
*/
@Override
public Object getItem(int position) {
return null;
}
/**
* @param position
* @return
*/
@Override
public long getItemId(int position) {
return 0;
}
/**
* @param <T>
*/
public static abstract class CommonViewHolder<T> {
/**
* @param item
*/
public abstract void setItem(T item);
}
}
四、Sample
adapter = new CommonBaseAdapter<Image>(context, R.layout.grid_item_similar_img, datas, SimilarImgGridViewHolder.class);
similarImgGridView.setAdapter(adapter);
static class SimilarImgGridViewHolder extends CommonBaseAdapter.CommonViewHolder<Image> {
private ImageView imageView;
public SimilarImgGridViewHolder(View convertView) {
((CardView)convertView).setPreventCornerOverlap(false);
imageView = (ImageView) convertView.findViewById(R.id.iv_similar_img_grid_item);
}
@Override
public void setItem(Image item) {
ImageLoader.getInstance().displayImage(item.getDownloadUrl(), imageView);
}
}
五、弯路
因为 CommonViewHolder 只有 CommonBaseAdapter 才会用到,所以想把前者写成后者的内部类。然后问题就来了。利用反射实例化自定义的CommonViewHolder 的时候总是 Null。然后看文档,看到这句话,**If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.**然后就明白了,如果没有把 CommonViewHolder修饰为 static 的话,它的构造器会有个隐性参数—CommonBaseAdapter。(很好理解,非静态内部类会持有外部类的引用嘛)。这样的话,利用反射查找参数为 View的构造器的时候,就找不到,自然也就无法实例化。把CommonViewHolder修饰为 static解决问题。