一个项目中一般会使用到多个ListView,在看了慕课网的“打造万能适配器BaseAdapter”之后,我第一次发现原来BaseAdapter被封装过后再使用是如此地简单,下面我记录一下封装的全过程:
1.因为ListView中要使用到ViewHolder来避免多次组件重复加载的情况,所以这里首先把ViewHolder封装成一个对象:
import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
public class ViewHolder {
/*
* 使用SparseArray将读取速度提高,因为这个类是android包中的类,
* 它比HashMap还要快,这个在android的API中有说道:
* It is intended to be more memory efficientthan
* using a HashMap to map Integers to Objects
*/
private SparseArray<View> views;
/*
* position的保存为了以后如果要保存的是CheckBox这样的组件的时候,
* 会出现选中一个就选中了多个的现象。
*/
private int position;
/*
* 保存BaseAdapter中getView方法的convertView,用来在这里findViewById组件
*/
private View convertView;
/**
* 构造方法,这里接收的参数都是在BaseAdapter的getView方法中的参数。
* @param context
* @param parent
* @param layoutId
* @param position
*/
public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
/*
* 完成各种初始化
*/
this.position = position;
views = new SparseArray<View>();
convertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
/*
* 把传统的setTag放在,因为第一次初始化ViewHolder的时候
* convertView也是第一次创建。
*/
convertView.setTag(this);
}
/**
* 得到ViewHolder
* @param context 传入的context,如果没有创建过convertView和ViewHolder,那么就要用到这个参数来创建ViewHolder和convertView
* @param convertView getView中的convertView
* @param parent getView中的ViewGroup,用来初始化convertView
* @param layoutId ListView中的每一个Item布局
* @param position 表示这是第几个列表项
* @return ViewHolder对象
*/
public static ViewHolder getViewHolder(Context context, View convertView
, ViewGroup parent, int layoutId, int position) {
/*
* 如果convertView为空,那么构造一个ViewHolder就行了,因为构造方法里实现了convertView的初始化
*/
if (convertView == null) {
return new ViewHolder(context, parent, layoutId, position);
} else {
/*
* 如果不为空,那么只需要getTag即可
*/
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.position = position;
return holder;
}
}
/**
* 通过组件的id获取组件,因为组件的父类都是View,所以这里用了泛型
* @param viewId 组件的id
* @return 该组件View
*/
public <T extends View> T getView(int viewId) {
/*
* 如果改组件已经初始化,那么SparseArray一定保存了改组件,
* 所以只需要判断从SparseArray获取指定viewId的组件如果不为空,就新建,否则就返回即可
*/
View view = views.get(viewId);
if (view == null) {
view = convertView.findViewById(viewId);
views.append(viewId, view);
}
return (T) view;
}
/**
* 返回convertView,因为BaseAdapter中的getView方法最后要返回convertView,
* 所以也把返回封装到这里来
* @return convertView
*/
public View getConvertView() {
return convertView;
}
/**
* 为指定组件TextView来setText,这里使用了链式编程,返回的是ViewHolder本身
* @param id 该TextView的id
* @param str 要设置的内容
* @return ViewHolder
*/
public ViewHolder setText(int id, String str) {
((TextView)getView(id)).setText(str);
return this;
}
/**
* 为CheckBox设置内容
* @param id
* @param str
* @return
*/
public ViewHolder setCheckBoxText(int id, String str) {
((CheckBox)getView(id)).setText(str);
return this;
}
/**
* 为Button设置内容
* @param id
* @param str
* @return
*/
public ViewHolder setButtonText(int id, String str) {
((Button)getView(id)).setText(str);
return this;
}
}
2.新建一个CommonAdapter来实现通用的Adapter,这个类是抽象类:
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public abstract class CommonAdapter<T> extends BaseAdapter {
protected Context context;
protected List<T> datas;
protected int layoutId;
protected LayoutInflater inflater;
public CommonAdapter(Context context, List<T> datas, int layoutId) {
// TODO Auto-generated constructor stub
this.context = context;
this.datas = datas;
this.layoutId = layoutId;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return datas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datas.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
/**
* 这里把getView都封装起来了,因为这里的ViewHolder和返回值都是多次重复的
* 所以用户只需要实现的是往ViewHolder中的组件设置内容或者监听器这些即可,那
* 么这里使用了抽象类的回调方法来实现,对外提供一个convert方法,这个方法就是
* 知道了viewHolder对象,但是工具类的类型不知道,这里我们使用了泛型,用户只
* 需要实现这个抽象方法,然后怎么设置就要看用户的实际需求了
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = ViewHolder.getViewHolder(context, convertView, parent, layoutId, position);
convert(viewHolder, datas.get(position));
return viewHolder.getConvertView();
}
/**
* 对外提供的抽象方法,用户只需要考虑如何实现这个方法即可
* @param viewHolder
* @param t
*/
public abstract void convert(ViewHolder viewHolder, T t);
}
3.有了这个CommonAdapter之后,那么用户需要继承的就不是BaseAdapter了,用户只需要继承这个CommonAdapter,实现里面的convert方法即可,这就大大减少了用户的代码量,因为很多操作都封装起来了,这对用户来说是透明的。实现的方法如下:
public class MyAdapterWithCommonHolder extends CommonAdapter<Bean> {
public MyAdapterWithCommonHolder(Context context, List<Bean> datas, int layoutId) {
// TODO Auto-generated constructor stub
super(context, datas, layoutId);
}
/**
* 实现convert方法,在实现这个方法的时候就,第二个参数就不是泛型类了
* 而是一个具体的类
*/
@Override
public void convert(ViewHolder viewHolder, final Bean t) {
// TODO Auto-generated method stub
/*
* 里面的操作用户自行设置,这里可以添加监听器,设置内容等
* 下面给出一个实例
*/
viewHolder.setText(R.id.tv, t.getTxt())
.setCheckBoxText(R.id.cb, t.getTxt())
.setButtonText(R.id.btn, t.getTxt());
final CheckBox cb = viewHolder.getView(R.id.cb);
cb.setChecked(t.isCheck());
cb.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
t.setCheck(cb.isChecked());
}
});
}
}
4.接下来只要使用listView的setAdapter方法设置Adapter即可
从上面可以看出,用户自己实现的代码就只有convert方法,用户甚至可以直接用匿名来实现,例如:
lv.setAdapter(new CommonAdapter<Bean>(MainActivity.this, datas) {
public void convert(ViewHolder viewHolder, Bean t) {
viewHolder.setText(R.id.tv, t.getTxt());
};
});
这里的lv代表listView。