自定义通用Adapter适配ListView GridView

本文介绍了一种自定义Adapter的封装方案,适用于Android开发中的多种布局类型。通过继承BaseAdapter并利用ViewHolder模式,简化了ListView、GridView适配器的编写过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

相信做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等),大家在实际使用的时候,可以根据项目添加这些方法

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值