关于android中ListView的Adapter如何设计能通用的一些看法

我们都知道,在安卓中使用ListView显示多条数据的时候,必须要用一个适配器作为Data和View的桥梁,这种设计非常好, 能很简单就把ui和data分离开来,为ui的复用和维护代码提供方便。
但是每次写一个适配器,都要实现一大堆的重复逻辑,下面是一个常规的实现:
foo_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android"    
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">   
 <TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="今天好天气" />
<TextView
android:id="@+id/txtContent"
android:layout_marginTop="10dp"
tools:text="我是一名android开发者"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

适配器:

public class FooNormalAdapter extends BaseAdapter {

    private List<FooBean> datas = new ArrayList<>();
    private Context _context;

    public FooNormalAdapter(Context context) {
        this._context = context;
    }

    public Context getContext() {
        return _context;
    }

    public void setDataSource(List<FooBean> fooBeens) {
        setDataSource(fooBeens,true);
    }

    public void setDataSource(List<FooBean> fooBeens,boolean isClear) {
        if(isClear) this.datas.clear();
        this.datas.addAll(fooBeens);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object 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) {
        FooViewHolder viewHolder = null;
        if (convertView == null) {
            convertView = View.inflate(getContext(), R.layout.foo_item, null);
            convertView.setTag(new FooViewHolder(convertView));
        } else {
            viewHolder = (FooViewHolder) convertView.getTag();
        }
        FooBean fb = (FooBean) getItem(position);
        viewHolder.txtTitle.setText(fb.getTitle());
        viewHolder.txtContent.setText(fb.getContent());
        return convertView;
    }

    public static class FooViewHolder {
        private TextView txtTitle;
        private TextView txtContent;

        public FooViewHolder(View convertView) {
            this.txtTitle = (TextView) convertView.findViewById(R.id.txtTitle);
            this.txtContent = (TextView) convertView.findViewById(R.id.txtContent);
        }
    }
}

嗯,看起来没有什么问题,但是如果有十个adapter,就要写十次这种无意义的代码,我们不能干体力活啊~怎么办?
先分析一下adapter需要哪些元素:
1.首先要inflateView就必须用到Context.
2.需要一个数组来存储用于显示的数据源
3.需要一个viewholder来优化程序性能
4.可能有不同的viewType
分析完这个,代码随之而来:

public abstract class LBaseAdapter<E, V extends LBaseAdapter.BaseViewHolder> extends BaseAdapter {

    private Context context;
    private List<E> dataSource = new ArrayList<>(); //初始化一个防止getCount()空指针

    public LBaseAdapter(Context context) {
        this.context = context;
    }

    public Context getContext() {
        return context;
    }

    //替换原有数据源
    public void setDataSource(List<E> dataSource) {
        setDataSource(dataSource,true);
    }

    //如果isClear==true,则替换原有数据源,否则加到数据源后面
    public void setDataSource(List<E> dataSource, boolean isClear) {
        if (isClear) this.dataSource.clear();
        this.dataSource = dataSource;
        notifyDataSetChanged();
    }

    //只加一个数据
    public void addData(E data) {
        this.dataSource.add(data);
        notifyDataSetChanged();
    }

    //通过下标移除一条数据
    public void removeData(int position) {
        this.dataSource.remove(position);
        notifyDataSetChanged();
    }

    //通过对象移除一条数据
    public void removeData(E data) {
        this.dataSource.remove(data);
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return this.dataSource.size();
    }


    @Override
    public E getItem(int position) {
        return this.dataSource.get(position);
    }


    @Override
    public long getItemId(int position) {
        return position;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        V viewHolder = null;
        if (convertView == null) {
            viewHolder = createViewHolder(position, parent);
            if (viewHolder == null || viewHolder.getRootView() == null) {
                throw new NullPointerException("createViewHolder不能返回null或view为null的实例");
            }
            convertView = viewHolder.getRootView();
            convertView.setTag(viewHolder);
        }else{
            viewHolder = (V) convertView.getTag();
        }
        //给当前复用的holder一个正确的position
        viewHolder.setPosition(position);
        bindViewHolder(viewHolder,position,getItem(position));
        return viewHolder.getRootView();
    }

    protected abstract V createViewHolder(int position, ViewGroup parent);

    protected abstract void bindViewHolder(V holder,int position, E data);

    public static class BaseViewHolder {
        private View rootView;
        private SparseArray<View> viewCache = new SparseArray<>();
        private int position = -1;

        public View getRootView() {
            return rootView;
        }

        void setPosition(int position) {
            this.position = position;
        }

        public int getPosition() {
            return position;
        }

        public BaseViewHolder(View rootView) {
            this.rootView = rootView;
        }

        public <R> R getView(@IdRes int viewID) {
            View cachedView = viewCache.get(viewID);
            if(null == cachedView) {
                cachedView = rootView.findViewById(viewID);
                viewCache.put(viewID, cachedView);
            }
            return (R) cachedView;
        }
    }
}

1.我加一个构造函数,强制传context,并提供getContext()方法
2.加一个泛型E,允许子类提供随意实体类型
3.加一个BaseViewHolder,并提供一些常用方法
4.重写getView(),在父类里面把复用逻辑搞定,并提供两个抽象方法用于让子类提供viewholder,和绑定具体数据,嗯,我是仿着RecyclerView来的。:)
ok,这个通用的父类怎么使用呢?
代码说话:

public class FooSuperAdapter extends LBaseAdapter<FooBean, LBaseAdapter.BaseViewHolder> {

    public FooSuperAdapter(Context context) {
        super(context);
    }

    @Override
    protected BaseViewHolder createViewHolder(int position, ViewGroup parent) {
        return new BaseViewHolder(View.inflate(getContext(), R.layout.foo_item,null));
    }

    @Override
    protected void bindViewHolder(BaseViewHolder holder, int position, FooBean data) {
        TextView txtTitle = holder.getView(R.id.txtTitle);
        TextView txtContent = holder.getView(R.id.txtContent);

        txtTitle.setText(data.getTitle());
        txtContent.setText(data.getContent());
    }
}

之前的一大堆东西,现在都不用关心了, 只管设置itemview,和绑定数据就好了,是不是好看多了呢?
通用adapter用于了泛型,如果不了解可以留言,我将出一个泛型的专题来讨论希望我的博客能帮到你 :)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容