Android适配器进阶之三(抽象分类适配器)

Android多类型抽象适配

我的CSDN博客

我的GitHub

我的GitHub博客


适配器的总结和使用

最新源代码下载

访问密码 9b16

更新:结合单类型抽象适配和分类适配的相关更新,对多类型抽象适配器进行了更新,以下代码是最新的。

上一篇文章介绍了如何进行分类适配,看过的小伙伴可以虽然比较完美的实现了分类适配以及复用,但是代码相当繁琐,我们有两种类型时已经出现了多层if嵌套,如果有三四种类型,估计自己都要转晕了,而且就像我们对单类型适配器抽象时做的,避免重复代码!如果没看过上一篇,建议浏览一下,这里写的很多都是基于第一篇的。

1.抽象ViewHolder

public static class MultiViewHolder extends ViewHolder{
        /**
         * SparseArray
         */
        private SparseArray<View> cacheViews;
        private View itemView;

        public MultiViewHolder(View itemView, int viewCount) {
            super();
            this.itemView = itemView;
            cacheViews = new SparseArray<View>(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;
        }
}
是不是发现跟第一篇的ViewHolder一模一样,本身ViewHolder就是维护UI的一个类,这里使用稀疏数组存储UI控件,又包含了获取控件的方法本身已经很完善了,后面我们会对ViewHolder进行进一步的完善附加功能。

2.获取类型的方法getType()

我们在分类适配的时候不可避免的要获得数据的对象,以此作为根据装载不同的布局文件,可能有人会说,在写bean类时加一个获取类型的方法就ok了,那么我们又如何保证在抽象的父类中可以直接调用这个方法呢,强制类实现一个方法!接口

/**
 1. 接口,分类适配的对象需要实现的接口,目的是约束实体类实现getType方法
 2. 
 3. @author chendong
 4. 
 */
public interface MultiEasyAdapterInterface {
    public int getType();
}
没什么好说的,接口很简单

3.抽象分类适配器

适配器的抽象稍微复杂一点

3.1定义类

  1. 我们需要避免在子类中避免重复编码,同时在子类实现自己的方法,使用抽象父类。
  2. 我们不了解传递进来的数据是什么类型,使用泛型。
  3. 我们需要限制传进来的数据必须实现获得获得子类的方法,所以必须要求数据实现MultiEasyAdapterInterface接口,使用泛型限定符确定上限。
public abstract class MultiEasyAdapter<T extends MultiEasyAdapterInterface>
        extends BaseAdapter {}

3.2成员变量

根据传统的写法,LayoutInflater 和数据集自然是必不可少的
private LayoutInflater layoutInflater;
private List<T> datas;
分析一下其他成员,根据上一篇传统分类适配的写法,我们姑且忽略类型的差异
  1. 每个类型需要一个布局资源文件id
  2. 每个类型布局文件中字UI控件的个数(用来优化)
  3. 每个类型一个唯一的键值(用来解决复用Item空指针的问题)
    ####我们用一个实体类存储这些信息
**
 * 存储类型信息的实体类
 *
 * @author chendong
 * @功能:分类适配器配置信息实体类
 */
public class MultiEasyAdapterEntity {

    /**
     * @param type
     *            item类型,int类型变量,Item是什么类型的就填写什么类型
     * @param resId
     *            资源id,对应类型的资源id,你需要装载的资源文件的ID
     * @param viewCount
     *            资源文件中对应的需要获取的视图的个数
     */
    public MultiEasyAdapterEntity(int type, int resId, int viewCount) {
        super();
        this.resId = resId;
        this.viewCount = viewCount;
    }

    private int type;
    private int resId;
    private int viewCount;

    public MultiEasyAdapterEntity() {
        super();
    }
}
可能有点繁琐,type和viewCount 可以省略掉,加入type是为了更灵活的获得数据类型,viewCount则是为了优化SparseArray,综上第三个成员变量,就是使用type作为键,MultiEasyAdapterEntity作为值的一个SparseArray,他的作用就是存储不同类型的数据适配时需要的配置信息,有点类似配置文件的意思。
private SparseArray<MultiEasyAdapterEntity> Res4Type;

3.3完善代码

结合分类适配的方法,变量我们已经存储到了SparseArray中,所以代码就很清晰了
/**
 * version 2<br/>
注意事项:类型数为n时,那么定义的类型必须在0-n-1之间,这是使用listview自带缓存的要求。
 * 抽象适配器升级版,可以进行分类适配,使用了模板方法模式,将设置item显示内容的部分抽象到了类外<br/>
 * @param <T> <br/>必须实现MultiEasyAdapterInterface接口{@link MultiEasyAdapterInterface}<br/>
 * @author chendong
 */
public abstract class MultiEasyAdapter<T extends MultiEasyAdapterInterface>
        extends BaseAdapter {
    private LayoutInflater layoutInflater;
    private List<T> datas;
    private SparseArray<MultiEasyAdapterEntity> Res4Type;
    private Context context;

    /**
     * @param context  上下文对象
     * @param datas    数据集
     * @param Res4Type 资源配置文件{@link MultiEasyAdapterEntity}this is like a config entity
     */
    public MultiEasyAdapter(Context context, List<T> datas,
                            SparseArray<MultiEasyAdapterEntity> Res4Type) {
        super();
        this.layoutInflater = LayoutInflater.from(context);
        this.datas = datas;
        this.Res4Type = Res4Type;
        this.context = context;
    }


    protected Context getContext(){
        return context;
    }
    protected List<T> getDatas(){
        return datas;
    }
    public void swapData(List<T> datas){
        this.datas = datas;
        notifyDataSetChanged();
    }

    @Override
    public int getViewTypeCount() {
        return Res4Type.size();
    }

    @Override
    public int getItemViewType(int position) {
        return datas.get(position).getType();
    }

    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;
        /* get the type*/
        int type = datas.get(position).getType();
        if (convertView == null) {
            int resId = Res4Type.get(type).getResId();
            convertView = layoutInflater.inflate(resId, parent, false);
            holder = new ViewHolder(convertView, Res4Type.get(type)
                    .getViewCount());
            convertView.setTag(holder);
            bindListener4View(holder,datas.get(position), type, position);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        bindData4View(holder,datas.get(position), type, position);
        return convertView;
    }

    /**
     * 绑定数据
     * bind data
     *
     * @param holder the viewholder
     * @param type data's type
     * @param data data
     */
    public abstract void bindData4View(ViewHolder holder, T data, int type,int pos);

    /**
     * 绑定监听
     * bind listener
     *
     * @param holder the viewholder
     * @param type data's type
     * @param pos position
     */
    public abstract void bindListener4View(ViewHolder holder, T data, int type,int pos);
}

测试

listview = (ListView) findViewById(R.id.listview);
        list = new ArrayList<Student>();
        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));
        }
        SparseArray<MultiEasyAdapterEntity> sparseArray = new SparseArray<MultiEasyAdapterEntity>(
                3);
        sparseArray.put(1, new MultiEasyAdapterEntity(1, R.layout.item_type1,
                4));
        sparseArray.put(2, new MultiEasyAdapterEntity(2, R.layout.item_type2,
                4));
        sparseArray.put(3, new MultiEasyAdapterEntity(3, R.layout.item_type3,
                4));
        listview.setAdapter(new MultiEasyAdapter<Student>(
                getApplicationContext(), list, sparseArray) {

            @Override
            public void bindData4View(ViewHolder holder,
                    Student data, int type) {
                ((TextView) holder.getView(R.id.tv_name)).setText(data
                        .getName());
                ((TextView) holder.getView(R.id.tv_sex)).setText(data.getSex());
                ((TextView) holder.getView(R.id.tv_age)).setText(data.getAge()
                        + "");
                if (type == 3)
                    ((TextView) holder.getView(R.id.tv_type)).setText(data
                            .getType()+"");
            }
@Override
            public void bindListener4View(ViewHolder holder,
                    Student data, int type) {
            //绑定监听事件
            }
        });
ps:
1.Student类,四个属性,name,age,sex,type(1,2,3)实现了MultiEasyAdapterInterface接口,这是必须的
2.根据type类型,将配置信息放到稀疏数组中,设置到适配器里
3.在抽象方法中根据type设置不同的类型数据

5.效果

大家可以看到三中布局适配的没有问题,只有蓝色背景的布局显示了type,他是类型3的数据

图片

6.总结

到此适配器进阶就基本结束了,使用单类型抽象适配器,分类抽象适配器可以很简单地进行数据适配操作,我是把它整理成了工具类来使用的,后面一片文章会进行一些总结和优化,并上传源代码,希望有不足的地方大家指正。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值