Android单类型抽象适配器

我的CSDN博客


我的GitHub


我的GitHub博客


适配器的总结和使用


最新源代码下载

访问密码 9b16

使用抽象适配器旨在简化开发适配器的过程,避免编写大量的重复代码

更新2015.11.14

1.更新方法,可以在子类中获取上下文,获取数据集。

2.添加了绑定监听的方法,不在数据绑定的部分进行事件的绑定以避免多次绑定事件。

3.更新ViewHolder方法,将在后文中介绍

4.以下代码是最新版本。

在Android中最西最重要的控件就是ListView,在一个项目中可能出现在任何地方,显示各种各样的数据,所以写好适配器是很重要的。

通常我们写适配器都会进行优化,使得Item可以进行复用,曾经当我发现复用的写法时一度感叹设计的重要性,使用ViewHolder复用Item将会使效率大大提高,如果传统适配器的优化不了解建议先去查一查。

言归正传,介绍一下适配器的抽象,在我们做项目的时候会有很多很多地方使用ListView,也就意味着需要写很多很多的适配器,当我们写的项目很大时就会很烦很烦,每次都要写同样的代码片实现类似的功能,所以我们就有必要对传统的适配器抽象一下。

我们先来看看传统适配器的优化写法

public class FirstpageGridAdapter extends BaseAdapter {    /* gridview数据 */    private String[] choices = new String[] { "新房", "二手房", "租房", "资讯", "打折优惠",            "最新开盘", "房贷计算", "更多" };    private int[] images = new int[] { R.drawable.selector_xinfang,            R.drawable.selector_ershou, R.drawable.selector_zufang,            R.drawable.selector_zixun, R.drawable.selector_youhui,            R.drawable.selector_kaipan, R.drawable.selector_calculator,            R.drawable.selector_more };    private LayoutInflater layoutInflater;    private onClickChildIbListener listener;    public FirstpageGridAdapter(Context context, onClickChildIbListener listener) {        super();        this.layoutInflater = LayoutInflater.from(context);        this.listener = listener;    }    public interface onClickChildIbListener {        public void clickChild(int pos,View view);    }    public int getCount() {        return choices.length;    }    public Object getItem(int position) {        return choices[position];    }    public long getItemId(int position) {        return position;    }    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder holder = null;        if (convertView == null) {            holder = new ViewHolder();            convertView = layoutInflater.inflate(R.layout.item_main_grid,                    parent, false);            holder.ib = (ImageButton) convertView                    .findViewById(R.id.item_main_grid_ib);            holder.tv = (TextView) convertView                    .findViewById(R.id.item_main_grid_tv);            convertView.setTag(holder);        } else {            holder = (ViewHolder) convertView.getTag();        }        holder.ib.setBackgroundResource(images[position]);        holder.tv.setText(choices[position]);        final int pos = position;        holder.ib.setOnClickListener(new OnClickListener() {            public void onClick(View v) {                listener.clickChild(pos,v);            }        });        return convertView;    }    private class ViewHolder {        ImageButton ib;        TextView tv;    }}
核心代码就是getVIew()方法,在里面我们进行Item的复用,而相对于其他方法就会显得很多余,因为每个适配器都在重复相同的代码

1.抽象ViewHolder

我们可以分析一下ViewHolder类,它维护一个Item的一组UI组件,如果我们要使用使用通用的ViewHolder就会遇到一个问题,你不知道不同的布局有什么组件在里面,如何维护一组UI呢,数组?链表?或者Map?当查找View时我们使用id来进行查找,那么想在ViewHolder中查找View就需要使用id作为键,我们选择使用SparseArray,也就是稀疏数组,什么是稀疏数组,使用id(大整数)作为键值存储数据会造成大多数的未被使用,如果使用一般的表存储会造成很大的浪费,稀疏数组对数组进行了压缩,节约了很大空间,详见这里

private SparseArray cacheViews;

使用稀疏数组存储UI控件以后我们需要一个获取UI控件的方法,方法很清晰的,itemView是传递进来的父控件,就是convertView从中根据id获取控件

@Override        public View getView(int resId) {            View v = cacheViews.get(resId);            if (v == null) {                v = itemView.findViewById(resId);                if (v != null) {                    cacheViews.put(resId, v);                }            }            return v;        }

ViewHolder完整代码,这里传入了一个viewCount,为什么呢?就像ArrayList一样,不定义空间大小时初始容量16,超出空间大小时每次增加25,但就是初始容量的16对于我们来说已经是浪费了,一个Item不可能有16个控件那么多。指定大小提高内存使用。

/**     *      * @author chendong     * 用来实现复用加载的单类型ViewHolder     *     */    public static class SingleViewHolder{        /**         * 使用SparseArray         */        private SparseArray cacheViews;        private View itemView;        public SingleViewHolder(View itemView, int viewCount) {            super();            this.itemView = itemView;            cacheViews = new SparseArray(viewCount);        }        @Override        public View getView(int resId) {            View v = cacheViews.get(resId);            if (v == null) {                v = itemView.findViewById(resId);                if (v != null) {                    cacheViews.put(resId, v);                }            }            return v;        }    }

2.抽象适配器

传统的适配器有太多的重复代码需要编写,我们可以把重复的代码在父类中编写好,使得子类可以直接复用,!使用抽象父类

/** 1. 抽象适配器,使用了模板方法模式,将设置item显示内容的部分抽象到了类外 这是单类型的抽象适配 2.  3. @author chendong 4.  5. @param  6.            泛型 */public abstract class SingleEasyAdapter<T> extends BaseAdapter {    private LayoutInflater layoutInflater;    private int resId;    private List datas;    private int viewCount = 5;    /**     * @param context     *            上下文对象,建议使用getApplicationContext();     * @param resId     *            item布局id     * @param datas     *            数据集     * @param viewCount     *            item中的view个数,用来优化SparseArray     */    public SingleEasyAdapter(Context context, int resId, List datas,            int viewCount) {        super();        this.layoutInflater = LayoutInflater.from(context);        this.resId = resId;        this.datas = datas;        this.viewCount = viewCount;    }    public int getCount() {        return datas.size();    }    public Object getItem(int position) {        return datas.get(position);    }    public long getItemId(int position) {        return position;    }    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder holder = null;        if (convertView == null) {            convertView = layoutInflater.inflate(resId, parent, false);            holder = new ViewHolder(convertView, viewCount);            convertView.setTag(holder);            //在这里绑定监听,避免重复绑定。            bindListener4View(holder, datas.get(position), position);        } else {            holder = (ViewHolder) convertView.getTag();        }        bindData4View(holder, datas.get(position), position);        return convertView;    }    /**     * 绑定数据     *     * @param holder     * @param data     */    public abstract void bindData4View(ViewHolder holder, T data, int pos);    /**     * 绑定监听     *     * @param holder     * @param pos     */    public abstract void bindListener4View(ViewHolder holder, T data, int pos);    }

1.父类的getView()方法中使用了一个抽象方法,使用了一个设计模式模板方法模式,将方法的实现推迟到了子类中
2.泛型,传递的数据类型是不确定的,使用泛型可以解决这个问题,泛型的使用不了解的建议搜一下,后面深入的介绍会使用更复杂的泛型

完整代码

/** * 抽象适配器,使用了模板方法模式,将设置item显示内容的部分抽象到了类外 这是单类型的抽象适配 * * @param  泛型 * @author chendong */public abstract class SingleEasyAdapter<T> extends BaseAdapter {    private LayoutInflater layoutInflater;    private int resId;    private List datas;    private int viewCount = 5;    private Context context;    /**     * @param context   上下文对象,建议使用getApplicationContext();     * @param resId     item布局id     * @param datas     数据集     * @param viewCount item中的view个数,用来优化SparseArray     */    public SingleEasyAdapter(Context context, int resId, List datas,                             int viewCount) {        super();        this.layoutInflater = LayoutInflater.from(context);        this.resId = resId;        this.datas = datas;        this.viewCount = viewCount;        this.context = context;    }    public Context getContext() {        return this.context;    }    public List getData() {        return datas;    }    public void swapData(List datas) {        this.datas = datas;        notifyDataSetChanged();    }    public int getCount() {        return datas.size();    }    public Object getItem(int position) {        return datas.get(position);    }    public long getItemId(int position) {        return position;    }    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolder holder = null;        if (convertView == null) {            convertView = layoutInflater.inflate(resId, parent, false);            holder = new ViewHolder(convertView, viewCount);            convertView.setTag(holder);            //在这里绑定监听            bindListener4View(holder, datas.get(position), position);        } else {            holder = (ViewHolder) convertView.getTag();        }        bindData4View(holder, datas.get(position), position);        return convertView;    }    /**     * 绑定数据     *     * @param holder     * @param data     */    public abstract void bindData4View(ViewHolder holder, T data, int pos);    /**     * 绑定监听     *     * @param holder     * @param pos     */    public abstract void bindListener4View(ViewHolder holder, T data, int pos);}

测试

listview = (ListView) findViewById(R.id.listview);        list = new ArrayList();        for (int i = 0; i < 20; i++) {            if (i % 5 == 0)                list.add(new Student("name" + i, "age" + i, "sex", 3));            else                list.add(new Student("name" + i, "age" + i, "sex",                        i % 2 == 0 ? 1 : 2));        }        listview.setAdapter(new SingleEasyAdapter(                getApplicationContext(), R.layout.item_type1, list, 4) {            @Override            public void bindData4View(SingleViewHolder holder, Student data,int pos) {                ((TextView)holder.getView(R.id.tv_name)).setText(data.getName());                //此处省略若干代码            }@Override            public void bindListener4View(SingleViewHolder holder, Student data,int pos) {                //此处省略若干代码            }        });

效果

可以看到我们只用了很少的代码就完成了功能,而且很清晰,简单地适配不需要创建适配器类了,使用匿名的也很快就可以实现。演示有点丑,基本都实现了。

总结

将适配器抽象出来作为一个类库,再使用的时候就会简单很多很多,当然如果你的适配器数据很复杂,那么也可以继承抽象父类生成自己的类。!

更多相关文章

  1. android通讯录开发
  2. JAVA 学到什么水平就可以转战 Android(安卓)了?
  3. Android(安卓)listview的适配器以及各种监听、效率的提升
  4. Android(安卓)C2DM学习——客户端代码开发
  5. Android下拉列表框
  6. 测试驱动开发与Android
  7. Android(安卓)如何使用NDK开发OPenGL ES
  8. Android(安卓)studio下的Android(安卓)JNI调用以及动态链接库.so
  9. Android平台上最好的几款免费代码编辑器

随机推荐

  1. Android文字基线(Baseline)算法
  2. Android学习笔记之ImageSwitcher
  3. Android为ToolBar设置沉浸式状态栏及其相
  4. android开发之Dialog
  5. Android中的Handler总结 转载
  6. 2012年最有价值的Android开发精品文章荟
  7. API23以上的运行时权限
  8. 2016年3月android面试总结(2)
  9. Android悬浮窗操作使用总结
  10. Android(安卓)1.6 支持更多的屏幕大小和