對BaseAdapter的進一步封裝,使得BaseAdapter用起來更方便
阿新 • • 發佈:2019-01-04
一個專案中一般會使用到多個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。