自定义通用Adapter适配ListView GridView

来源:互联网 发布:windows 调试模式 编辑:程序博客网 时间:2024/06/07 14:59

概述

相信做Android开发的写得最多的就是ListView,GridView的适配器吧,对于Adapter一般都继承BaseAdapter复写几个方法,getView里面使用ViewHolder模式,其实大部分的代码基本都是类似的。
网上也有一些自定义Adapter的封装,本篇博客的Adapter的可以适应于多种类型的layout,也就是实现对话效果,而且代码和类设计都比较简单。

效果图,其中图片和文字是两个不同的layout:
这里写图片描述


类设计图

这里写图片描述
从上面的类图我们可以看到,直接继承BaseAdapter是类MutiplyCommonAdapter,根据类名就知道,这个类可以实现多种不同的布局。
我们把只能实现一种布局看成是该类的特殊情况,继承并且新建了类CommonAdapter。这个类是我们最常使用到的类。
至于ViewHolder是一个辅助类,在常见的getView()方法中,我们总是设置一个ViewHolder来进行缓存,这里也是做了这个工作,并且提供了一些快捷设置的方法,例如根据id设置文本,背景等,减少了我们使用findViewById()的次数。



MutiplyCommonAdapter

public abstract class MutiplyCommonAdapter<T> extends BaseAdapter{    protected Context mContext;    protected List<T> mDatas;    private int[] layoutId;    public MutiplyCommonAdapter(Context context, List<T> datas, int... layoutId){        this.mContext = context;        this.mDatas = datas;        this.layoutId = layoutId;    }    @Override    public int getCount(){        return mDatas.size();    }    @Override    public T getItem(int position){        return mDatas.get(position);    }    @Override    public long getItemId(int position){        return position;    }    @Override    public int getViewTypeCount() {        return layoutId.length;    }    @Override    public abstract int getItemViewType(int position);    @Override    public View getView(int position, View convertView, ViewGroup parent){        ViewHolder holder = ViewHolder.get(mContext, convertView, parent,                layoutId[getItemViewType(position)], position);        convert(holder, getItem(position),getItemViewType(position));        return holder.getConvertView();    }    //type是getItemViewType(position)的返回值    public abstract void convert(ViewHolder holder, T t,int type);}

观察上面的类,构造方法的最后传入了一个int[]类型的layout数组,根据layout数组的长度,我们就可以得到getViewTypeCount()的返回值。
另外getItemViewType(int position)需要使用者自己定义,因为实现根据需求各有不同,例如我们根据奇偶行实现不同的布局。
接下来最主要的一个方法就是getView()方法,里面使用了ViewHolder这个辅助类返回了ViewHolder,这样我们就不需要自己实现Viewholder了。
最后是convert()方法,这里是我们自己对控件进行操作的地方。


CommonAdapter

public abstract class CommonAdapter<T> extends MutiplyCommonAdapter<T>{    private CommonAdapter(Context context, List<T> datas, int... layoutId) {        super(context, datas, layoutId);    }    public CommonAdapter(Context context, List<T> datas, int layoutId) {        this(context, datas, new int[]{layoutId});    }    @Override    public int getItemViewType(int position) {        return 0;    }    public abstract void convert(ViewHolder holder, T t);    @Override    public void convert(ViewHolder holder, T t,int type){        convert(holder,t);    }}

CommonAdapter目的是提供单一布局的Adapter。
显然getItemViewType(int position)返回0;
convert(ViewHolder holder, T t)不需要再传入layout类型了,因为只有一种类型


ViewHolder

public class ViewHolder {    /** 控件引用缓存 */    private final SparseArray<View> mViews;    private final Context mContext;    private int mPosition;        private View mConvertView;    public ViewHolder(Context context, ViewGroup parent, int layoutId,int position){        mContext = context;        mPosition = position;        mViews = new SparseArray<View>();        mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,                false);        mConvertView.setTag(this);    }    public static ViewHolder get(Context context, View convertView,            ViewGroup parent, int layoutId, int position){        if (convertView == null)            return new ViewHolder(context, parent, layoutId, position);             ViewHolder holder = (ViewHolder) convertView.getTag();        holder.mPosition = position;        return holder;          }        /**     * 根据id获取控件     * @param viewId     * @return     */    @SuppressWarnings("unchecked")    public <T extends View> T getView(int viewId){        View view = mViews.get(viewId);        if (view == null){            view = mConvertView.findViewById(viewId);            mViews.put(viewId, view);        }        return (T) view;    }    public ViewHolder setText(int viewId, String value) {        TextView view = getView(viewId);        view.setText(value);        return this;    }        ...}

首先构造函数中,使用LayoutInflater根据layoutId创建了布局View,并且将当期ViewHolder设置View的Tag。
然后是get()方法里面,判断View是否被缓存,如果是直接返回,否则根据Tag获取。
另外提供了一个getView()方法用于方便使用者用过id获取控件对象。其实这个类里面有很多这种工具方法,没有一一贴出来,典型的就是setText(int viewId, String value)方法。


简单使用

创建一个MyAdapter来根据position实现两种布局

public class MyAdapter extends MutiplyCommonAdapter<String>{    public MyAdapter(Context context, List<String> datas, int... layoutId) {        super(context, datas, layoutId);    }    //volley    public RequestQueue requestQueue = Volley.newRequestQueue(mContext);    public CombineCache mCombineCache = new CombineCache(mContext);    public ImageLoader mImageLoader = new ImageLoader(requestQueue, mCombineCache);    @Override    public int getItemViewType(int position) {              return position%2;//根据奇偶行设置不同的布局    }    @Override    public void convert(final ViewHolder holder, String url, int type) {        switch (type) {        case 0:            loadImg(holder,url);            break;        case 1:            loadText(holder,url);            break;        default:            break;        }           }    //图片布局    private void loadImg(final ViewHolder holder,String url){        mImageLoader.get(url, new ImageListener() {                             @Override            public void onErrorResponse(VolleyError error) {                Log.i("cky", "error");                ImageView img = (ImageView)(holder.getView(R.id.img));                img.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_launcher));            }            @Override            public void onResponse(ImageContainer response, boolean isImmediate) {                ImageView img = (ImageView)(holder.getView(R.id.img));                img.setImageBitmap(response.getBitmap());            }        },400,400);    }    //文字布局    private void loadText(final ViewHolder holder,String url){        holder.setText(R.id.text,url);    }}

在Activity里面我这样使用

final MyAdapter myAdapter = new MyAdapter(this,arr,R.layout.listview_cell,R.layout.listview_cell2);//arr是图片地址listview.setAdapter(myAdapter);         



写在最后

通过对Adapter的简单封装,我们大大减少了自定义Adapter过程中的麻烦。网上实现多布局Adapter的设计,都是以单个布局Adapter为基类,然后提供一个辅助类来实现多布局。
这样的思路显然不直观,单个布局Adapter是多个布局Adapter的一种特殊情况,那么他们的继承关系就很清楚了。
在ViewHolder中,我们并没有实现setImage()之类的为控件设置图片的方法,因为对于图片的加载,有许多不同的策略(例如Volley,UIL等),大家在实际使用的时候,可以根据项目添加这些方法

源码下载

2 1
原创粉丝点击