MultiItem用法与详解-优雅的实现多类型RecyclerView Adapter

前言

RecyclerView是一个大家常用的列表控件,在列表中不免会出现多种类型的布局,这时adapter中多种类型的判断就会充满着switch的坏味道,可怕的是需求变更,增加或修改新的类型时,所有的改动都在adapter中进行,没有一个良好的扩展性。
MutliItem主要就是解决这些问题,在正常使用中做到了Adapter零编码,解放了复杂的Adapter类,本库提供了多类型和ViewHolder创建绑定的管理器,这样Adapter通过依赖倒置与列表中的多类型解耦,还提高了扩展性。在本库中不同实体类可以直接当成数据源绑定到adapter中,你不用去担心item type的计算,并且对每种类型的ViewHolder也做到了隔离。
本库的定位并不是大而全,但是会尽量做到简单易用。

源码地址

Github地址:MultiItem,请大家多多关注,更多更新会首先在GitHub上体现,也会在第一时间在本平台发布

效果截图

multi_item
multi_item
chat
chat

下一步要做什么

  • DataBinding特性支持
  • 录入界面的复用和封装的demo代码(录入业务较多同学可以多多关注)
  • 思考动画分割线等一些功能封装

用法

添加依赖

  • 配置gradle:

Project rootbuild.gradle中添加:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Module中添加(最新版本请在源码地址查看):

dependencies {
    compile 'com.github.free46000:MultiItem:0.9.3'
}
  • 或者你也可以直接克隆源码

多种类型列表用法

这里由于单一和多种类型写法上没有差别,所以就不单独贴出单一类型的列表代码了。
注册多种类型ViewHolderManager,并为adapter设置多种类型数据源:

//初始化adapter
BaseItemAdapter adapter = new BaseItemAdapter();
//为TextBean数据源注册ViewHolderManager管理类
adapter.register(TextBean.class, new TextViewManager());
//为更多数据源注册ViewHolderManager管理类
adapter.register(ImageTextBean.class, new ImageAndTextManager());
adapter.register(ImageBean.class, new ImageViewManager());

//组装数据源list
List<Object> list = new ArrayList<>();
list.add(new TextBean("AAA"));
list.add(new ImageBean(R.drawable.img1));
list.add(new ImageTextBean(R.drawable.img2, "BBB" + i));
       
//为adapter注册数据源list
adapter.setDataItems(list);

recyclerView.setAdapter(adapter);

ViewHolder管理类的子类TextViewManager类,其他类相似,下面贴出本类全部代码,是不是非常清晰:

public class ImageViewManager extends BaseViewHolderManager<ImageBean> {

    @Override
    public void onBindViewHolder(BaseViewHolder holder, ImageBean data) {
        //在指定viewHolder中获取控件为id的view
        ImageView imageView = getView(holder, R.id.image);
        imageView.setImageResource(data.getImg());
    }

    @Override
    protected int getItemLayoutId() {
        //返回item布局文件id
        return R.layout.item_image;
    }
}

至此本库的多种类型列表用法已经完成,并没有修改或继承RecyclerView Adapter类,完全使用默认实现BaseItemAdapter即可。

相同数据源对应多个ViewHolder(聊天界面)

这是一种特殊的需求,需要在运行时通过数据源中的某个属性,判断加载的布局,典型的就是聊天功能,相同消息数据对应左右两种气泡视图,在此处贴出注册时的关键代码,其他和多种类型列表类似:

//初始化adapter
BaseItemAdapter adapter = new BaseItemAdapter();

//为XXBean数据源注册XXManager管理类组合
adapter.register(MessageBean.class, new ViewHolderManagerGroup<MessageBean>(new SendMessageManager(), new ReceiveMessageManager()) {
    @Override
    public int getViewHolderManagerIndex(MessageBean itemData) {
        //根据message判断是否本人发送并返回对应ViewHolderManager的index值
        return itemData.getSender().equals(uid) ? 0 : 1;
    }
});

recyclerView.setAdapter(adapter);

设置点击监听

点击监听:

adapter.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(BaseViewHolder viewHolder) {
        //通过viewHolder获取需要的数据
        toastUser(String.format("你点击了第%s位置的数据:%s", viewHolder.getItemPosition()
        , viewHolder.getItemData()));
    }
});

长按监听:

adapter.setOnItemLongClickListener(new OnItemLongClickListener() {
    @Override
    public void onItemLongClick(BaseViewHolder viewHolder) {
        //通过viewHolder获取需要的数据
        toastUser(String.format("你长按了第%s位置的数据:%s", viewHolder.getItemPosition()
                , viewHolder.getItemData()));
    }
});

详解

主要流程

  • 为指定的数据源注册ViewHolderManager提供视图创建绑定等工作
  • 在列表创建的过程中通过数据源在ItemTypeManager找到对应的ViewHolderManager
  • 按照需要创建与刷新视图并对视图做一些通用处理

ViewHolder管理

ViewHolder管理源码类为ViewHolderManager,使用者会首先注册数据源和本实例的对应关系,由类型管理类提供统一管理。

  • 提供了参数类,会在adapter调用本类方法的时候传入并做出通用处理
  • 本类的设计使用泛型,是为了在后续回调方法中有更直观的类型体现,这也是强类型和泛型带来的好处,给人在编写代码的时候带来确定感
  • 本类为抽象类需要重写ViewHolder的创建与绑定方法,为了方便后续使用,写了一个简单的BaseViewHolderManager实现类,请读者根据业务自行决定是否需要使用更灵活的基类,这里贴出需要复写的两个方法,延续了Adapter中的命名规则,在使用中减少一些认知成本:
/**
 * 创建ViewHolder
 * {@link android.support.v7.widget.RecyclerView.Adapter#onCreateViewHolder}
 */
@NonNull
public abstract V onCreateViewHolder(@NonNull ViewGroup parent);

/**
 * 为ViewHolder绑定数据
 * {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}
 *
 * @param t 数据源
 */
public abstract void onBindViewHolder(@NonNull V holder, @NonNull T t);

ViewHolder管理组合(相同数据源对应多个ViewHolderManager)

组合管理源码类为ViewHolderManagerGroup,本实例需要一个ViewHolderManager集合,并增加通过数据源指定哪个ViewHolderManager的方法,使用者同样会注册数据源和本实例的对应关系,由类型管理类对本类中的ViewHolderManager集合进行统一注册管理。下面贴出关键代码:

 private ViewHolderManager[] viewHolderManagers;

/**
 * @param viewHolderManagers 相同数据源对应的所有ViewHolderManager
 */
public ViewHolderManagerGroup(ViewHolderManager... viewHolderManagers) {
    if (viewHolderManagers == null || viewHolderManagers.length == 0) {
        throw new IllegalArgumentException("viewHolderManagers can not be null");
    }
    this.viewHolderManagers = viewHolderManagers;
}

/**
 * 根据item数据源中的属性判断应该返回的对应viewHolderManagers的index值
 *
 * @param itemData item数据源
 * @return index值应该是在viewHolderManagers数组有效范围内
 */
public abstract int getViewHolderManagerIndex(T itemData);

类型管理

类型管理源码类为ItemTypeManager,通过数据源className ListviewHolderManager List两组集合对类型进行管理,并对Adapter提供注册和对应关系查找等方法的支持,这里并没有把这个地方设计灵活,如果有一些变化还是希望可以在ViewHolderManager做出适配。

  • 数据源一对一viewHolderManager时比较简单,关键代码:
 /**
 * 通过数据源`className List`和`viewHolderManager List`两组集合对类型进行管理
 *
 * @param cls     数据源class
 * @param manager ViewHolderManager
 * @see com.freelib.multiitem.adapter.BaseItemAdapter#register(Class, ViewHolderManager)
 */
public void register(Class<?> cls, ViewHolderManager manager) {
    register(getClassName(cls), manager);
}
  • 数据源一对多viewHolderManager时,关键代码:
/**
 * 通过group获取一组ViewHolderManager循环注册,并生成不同的className作为标识<br>
 * 其他类似{@link #register(Class, ViewHolderManager)}
 *
 * @param cls   数据源class
 * @param group 多个ViewHolderManager的组合
 * @see com.freelib.multiitem.adapter.BaseItemAdapter#register(Class, ViewHolderManagerGroup)
 */
public void register(Class<?> cls, ViewHolderManagerGroup group) {
    ViewHolderManager[] managers = group.getViewHolderManagers();
    for (int i = 0, length = managers.length; i < length; i++) {
        register(getClassNameFromGroup(cls, group, managers[i]), managers[i]);
    }
    itemClassNameGroupMap.put(getClassName(cls), group);
}

希望大家会喜欢,多多留言交流

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

推荐阅读更多精彩内容