一般情況,我們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()等指派操作。
如何擷取代碼?
git clone https://github.com/droid4j/anKataLite.git
本篇對應的标簽 v0.2
git checkout v0.2