Adapter

单布局

ListView/GridView + Adapter可以说是Android程序员最常用的组件了. 我们应该尽量简化Adapter的代码以提高开发效率. 首先来看看不做任何封装的Adapter需要哪些步骤:

  1. 继承自BaseAdpter

  2. 维护一个List数据并实现4个抽象方法

  3. 声明一个ViewHolder

  4. 在getView中判断convertView是否为空, 为空则inflate一个布局, 初始化ViewHolder, 初始化控件, 并将ViewHolder通过setTag设置到convertView中.

  5. 如果不为空则通过getTag将ViewHolder取出来

  6. 为控件设置值

可以看到需要做的事情还是很多的. utils包中有一个ViewHolder类, 此类可以代替自己声明的ViewHolder, 并且不用写将ViewHolder与convertView绑定的代码. 控件可以直接通过如下方式声明:

TextView textView = ViewHolder.get(convertView, R.id.textView);

ViewHolder中使用了SparseArray来缓存视图, 内部也是用过给convertView设置tag来达到缓存的目的. 并且通过泛型来避免类型转换. 整体逻辑很简单:

public class ViewHolder {
    @SuppressWarnings("unchecked")
    public static <T extends View> T get(View view, int id) {
        SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
        if (viewHolder == null) {
            viewHolder = new SparseArray<>();
            view.setTag(viewHolder);
        }
        View childView = viewHolder.get(id);
        if (childView == null) {
            childView = view.findViewById(id);
            viewHolder.put(id, childView);
        }
        return (T) childView;
    }
}

下面的内容详细可以参考 打造万能的ListView GridView 适配器

虽然有了ViewHolder能减少很多代码量, 但是这不是极限, 还可以继续减少. 比如内部维护的List数据, 另外三个抽象方法等都是可以不用写的. 这里我们引用GitHub上的一个开源工具 base adapter helper. 现在来看看如何使用base adapter helper:

可以调用如下方法修改Adapter的数据

base adapter helper的原理在此处就不赘述了, 参考 Android base-adapter-helper 源码分析与扩展. 使用base adapter helper可以减少大量重复的代码, 也可以通过扩展BaseAdapterHelper以适应自己的编程习惯.

接下来看看base adapter helper的getItemId方法的实现:

很多时候, getItemId的返回值都是position. 如果我们需要拿到实体类的id, 就必须这么写:

通常情况下, ListView中的某些控件是需要与网络API交互的, 而基本上网络API需要的只是一条数据的id. 因此我改造了getItemId方法, 在某些情况下, 此方法能直接返回实体类中的id.

上述的某些情况就是指T类型实现了IdObject接口. 以上就是对Android适配器的简化及优化. 但是base adapter helper使用虽然简单, 也还是有不足的地方. 比如不支持多布局, 只支持BaseAdapter, 不支持其他类型比如ViewPager的Adapter等.

以上就是单布局的基本原理与使用方法. 单布局是APP中最常用的, 下面介绍不是很常见的多布局的封装.

多布局

多布局是指一个适配器中使用一个以上的布局文件. 主要是依赖于覆写BaseAdapter中的getViewTypeCountgetItemViewType方法. 多布局使用了代理, 将convert方法代理给多个代理类去实现, 有兴趣的可以看这里, 这里只讲使用方法. 直接上代码:

效果:

效果

两个布局的差别就是一个TextView的gravity是left, 同时有margin值, 另一个的gravity为right, 没有margin值. 如果是双数则显示gravity是left的TextView, 否则显示gravity是right的TextView. 代码也比较好理解, 接下来看看Delegate接口的声明以及每个方法的含义:

ItemViewDelegate继承了BaseItemViewDelegate, 同时声明H为BaseAdapterHelper, 所以在不需要自定义AdapterHelper的情况下, 建议直接使用ItemViewDelegate:

RecyclerView

作为目前超级灵活超级NB的杀手级控件 - RecyclerView, 怎么能少了它呢? 直接上代码:

使用方法与ListView/GridView一样, 只不过继承从QuickAdapter变成RecyclerAdapter. 多布局也一样, 区别只是从QuickMultiAdapter换成RecyclerMultiAdapter. 下面看一个多布局的例子:

首先是效果:

效果

示例中有两种布局, 一种是日期, 一种是课程详情. 日期占满一行, 详情占半行. XML以及实体省略:

除了Adapter的代码外, 其他均是RecyclerView的基本用法, 如使用SpanSizeLookup来设置每个条目占用的单元数, 使用ItemDecoration来控制条目之间的间隔等.

Last updated

Was this helpful?