一般情况,我们ListView的Adapter,继续自BaseAdapter,很多方法都是重复性的工作,如:getItem(),getCount()等,为了更高效,我们要自己创建ViewHolder,用于缓存View。随着开发经验的积累,我们发现其中的一些套路,可以将这些不变的部分放到抽象类中,将变换的部分,写成抽象方法,这样,在使用时,直接继续自我们的CommonAdapter,复写抽象方法即可。
1、CommonAdapter
public abstract class CommonAdapter<T> extends BaseAdapter {
protected Context mContext;
private List<T> datas;
private int mLayoutId;
public CommonAdapter(Context context, List<T> datas, int layoutId) {
this.mContext = context;
this.datas = datas;
this.mLayoutId = layoutId;
}
@Override
public int getCount() {
return datas.size();
}
@Override
public T getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = ViewHolder.getViewHolder(mContext, convertView, mLayoutId);
convert(viewHolder, datas.get(position), position);
return viewHolder.getConvertView();
}
protected abstract void convert(ViewHolder viewHolder, T data, int position);
}
2、ViewHolder
public class ViewHolder {
private View convertView;
private SparseArray<WeakReference<View>> mViews;
public ViewHolder(Context context, View convertView, int layoutId) {
convertView = View.inflate(context, layoutId, null);
convertView.setTag(this);
this.convertView = convertView;
mViews = new SparseArray<>();
}
public static ViewHolder getViewHolder(Context context, View convertView, int layoutId) {
if (convertView == null) {
ViewHolder viewHolder = new ViewHolder(context, convertView, layoutId);
return viewHolder;
} else {
return (ViewHolder) convertView.getTag();
}
}
public View getConvertView() {
return convertView;
}
public ViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
if (tv != null) {
tv.setText(text);
}
return this;
}
public ViewHolder setImageRes(int viewId, int resId) {
ImageView iv = getView(viewId);
if (iv != null) {
iv.setImageResource(resId);
}
return this;
}
public ViewHolder setImageUrl(int viewId, String url) {
ImageView iv = getView(viewId);
if (iv != null) {
//这里要配合网络框架使用...
}
return this;
}
public <T extends View> T getView(int viewId) {
WeakReference<View> viewWeakReference = mViews.get(viewId);
View view = null;
if (viewWeakReference != null) {
view = viewWeakReference.get();
}
if (view == null) {
view = convertView.findViewById(viewId);
mViews.put(viewId, new WeakReference<View>(view));
}
return (T) view;
}
}
将之前在getView()中判断convertView的内容,移到了getViewHolder()方法中,然后提供了常用的setText()和setImage()方法,访问网络图片的,这里没写完。其他控件可以通过调用getView()方法进行内容设置。
还有两点,需要注意:
- setText()这几个方法,我们采用链式调用的方式,一行代码,设置任意多个控件。
- getView()的写法,我们不是简单的省去findViewById()这种操作,更重要的是,使用SparseArray对View的缓存,我们并没有直接将View放到集合中,而是用WeakReference对View进行了一层包装,防止内存泄漏。
3、如何用?
public class MyAdapter extends CommonAdapter<Person> {
public MyAdapter(Context context, List datas) {
super(context, datas, R.layout.item_layout);
}
@Override
protected void convert(ViewHolder viewHolder, Person data, int position) {
viewHolder.setText(R.id.item_tv, "测试")
.setText(R.id.activity_main, "hello");
}
}
说明:
- 只要指定泛型参数,就可以直接在convert()方法中设置相应的值了。
- 我们在封装CommonAdapter时,构造方法中有一个layoutId参数,它是用来设置item布局的,只要在新建的Adapter中,调用super()方法,并设置相应的布局即可,不用再抛给外面设置了。
- 在convert()方法中,只接调用viewHolder,就可以进行setText()等赋值操作。
如何获取代码?
本篇对应的标签 v0.2
git checkout v0.2