如何使用BaseRecyclerViewAdapterHelper实现单类型、多类型和节点类型列表界面

1,833 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

我们在Android应用开发中,列表控件RecyclerView基本每个项目都用到,那么你都怎么实现的呢?有人说,我用系统最原始的方式。No problem(没有问题),只要你不嫌麻烦。我一般使用BaseRecyclerViewAdapterHelper,闲话不多说,开始介绍用法。 首先要settings.gradle添加代码

dependencyResolutionManagement {
    repositories {
        maven {
            url "https://jitpack.io"
        }
    }
}

然后在app模块的build.gradle添加代码

dependencies {
    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.10'
}

单类型BaseQuickAdapter

package com.example.myapplication.adapter

import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication.R
import com.example.myapplication.entity.Entity

class SampleQuickAdapter : BaseQuickAdapter<Entity, BaseViewHolder>(R.layout.layout_normal_item) {

    override fun convert(holder: BaseViewHolder, item: Entity) {
        holder.setText(R.id.tv_text, item.text)
    }
}

单类型的列表是最简单的一种,首先需要继承BaseQuickAdapter,指定泛型类型Entity和BaseViewHolder,简单布局一般就使用BaseViewHolder就好,然后在构造方法中传入布局,重写convert()方法设置条目的数据。最后调用adapter.setList()方法设置数据。

<?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="wrap_content"
    android:orientation="horizontal"
    android:background="#FFF"
    android:padding="@dimen/dp_10">
    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

布局很简单,不用多说。

多类型BaseMultiItemQuickAdapter

package com.example.myapplication.adapter

import com.chad.library.adapter.base.BaseMultiItemQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication.R
import com.example.myapplication.entity.LetterEntity

class SampleMultiItemQuickAdapter : BaseMultiItemQuickAdapter<LetterEntity, BaseViewHolder>() {

    init {
        addItemType(LetterEntity.ITEM_TYPE_HEAD, R.layout.layout_head_item)
        addItemType(LetterEntity.ITEM_TYPE_CONTENT, R.layout.layout_content_item)
    }

    override fun convert(holder: BaseViewHolder, item: LetterEntity) {
        when (item.itemType) {
            LetterEntity.ITEM_TYPE_HEAD -> {

            }
            LetterEntity.ITEM_TYPE_CONTENT -> {

            }
        }
    }
}

多类型的适配器BaseMultiItemQuickAdapter用于简单的多类型场景。你需要让LetterEntity实现MultiItemEntity接口,然后通过判断Entity的itemType字段来决定显示什么内容。注意不要忘了在构造方法中调用addItemType()方法将所有支持的类型和布局对应起来。这种比如用于字母导航的列表。

多类型加强版BaseProviderMultiAdapter

BaseProviderMultiAdapter用于复杂业务的多类型业务,比如聊天房间界面的消息列表显示。使用BaseProviderMultiAdapter的好处就是要复用Provider。

package com.example.myapplication.adapter

import com.chad.library.adapter.base.BaseProviderMultiAdapter
import com.example.myapplication.R
import com.example.myapplication.entity.ChatMsg
import com.example.myapplication.provider.*

class FriendChatAdapter : BaseProviderMultiAdapter<ChatMsg>() {

    init {
        addItemProvider(WechatTextOrEmojiProvider(0, R.layout.layout_text_or_emoji_item))
        addItemProvider(WechatImgProvider(1, R.layout.layout_img_item))
        addItemProvider(WechatVoiceProvider(2, R.layout.layout_voice_item))
        addItemProvider(WechatVideoProvider(3, R.layout.layout_video_item))
        addItemProvider(WechatRedEnvelopeProvider(4, R.layout.layout_red_envelope_item))
        // 好友聊天需要语音/视频通话
        addItemProvider(WechatVoiceOrVideoRTCProvider(5, R.layout.layout_voice_or_video_rtc_item))
    }

    override fun getItemType(list: List<ChatMsg>, position: Int): Int {
        return list[position].getMsgType()
    }
}
package com.example.myapplication.adapter

import com.chad.library.adapter.base.BaseProviderMultiAdapter
import com.example.myapplication.R
import com.example.myapplication.entity.ChatMsg
import com.example.myapplication.provider.*

class GroupChatAdapter : BaseProviderMultiAdapter<ChatMsg>() {

    init {
        addItemProvider(WechatTextOrEmojiProvider(0, R.layout.layout_text_or_emoji_item))
        addItemProvider(WechatImgProvider(1, R.layout.layout_img_item))
        addItemProvider(WechatVoiceProvider(2, R.layout.layout_voice_item))
        addItemProvider(WechatVideoProvider(3, R.layout.layout_video_item))
        addItemProvider(WechatRedEnvelopeProvider(4, R.layout.layout_red_envelope_item))
    }

    override fun getItemType(list: List<ChatMsg>, position: Int): Int {
        return list[position].getMsgType()
    }
}
package com.example.myapplication.provider

import com.chad.library.adapter.base.provider.BaseItemProvider
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication.entity.ChatMsg

/**
 * 文字和表情😁。
 */
class WechatTextOrEmojiProvider(override val itemViewType: Int, override val layoutId: Int) : BaseItemProvider<ChatMsg>() {

    override fun convert(helper: BaseViewHolder, item: ChatMsg) {
        // 通过item判断是自己的还是对方发送的消息
        // 然后决定显示左边的界面还是右边的界面
    }
}

通过adapter的getItemType()方法的返回值对应provider的itemViewType属性。这种高内聚低耦合的写法将多类型布局用得有点惟妙惟肖的感觉了。

节点类型BaseNodeAdapter

BaseNodeAdapter用于可折叠的列表界面。这种类似于电脑版本QQ的聊天好友列表,也是经常使用到的一种列表界面。

package com.example.myapplication.adapter

import com.chad.library.adapter.base.BaseNodeAdapter
import com.chad.library.adapter.base.entity.node.BaseNode
import com.example.myapplication.provider.BranchNodeProvider
import com.example.myapplication.provider.LeafNodeProvider
import com.example.myapplication.R
import com.example.myapplication.entity.FirstNode
import com.example.myapplication.entity.SecondNode

class SampleNodeAdapter : BaseNodeAdapter() {

    init {
        addFullSpanNodeProvider(BranchNodeProvider(1, R.layout.layout_node_branch_item))
        addNodeProvider(LeafNodeProvider(2, R.layout.layout_node_leaf_item))
    }

    override fun getItemType(data: List<BaseNode>, position: Int): Int {
        val baseNode = data[position]
        if (baseNode is FirstNode) {
            return 1
        }
        if (baseNode is SecondNode) {
            return 2
        }
        return 0
    }
}

树枝节点的实体类继承BaseExpandNode而树叶节点的实体类则直接继承BaseNode,调用addFullSpanNodeProvider()方法来创建可展开的节点,调用addNodeProvider()方法来创建被展开的内容节点。

package com.example.myapplication.provider

import com.chad.library.adapter.base.entity.node.BaseNode
import com.chad.library.adapter.base.provider.BaseNodeProvider
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication.R
import com.example.myapplication.entity.FirstNode

class BranchNodeProvider(override val itemViewType: Int, override val layoutId: Int) : BaseNodeProvider() {

    override fun convert(helper: BaseViewHolder, item: BaseNode) {
        if (item is FirstNode) {
            helper.setText(R.id.tv_type, item.type)
            helper.setText(R.id.tv_name, item.name)
        }
    }
}
package com.example.myapplication.provider

import com.chad.library.adapter.base.entity.node.BaseNode
import com.chad.library.adapter.base.provider.BaseNodeProvider
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.example.myapplication.R
import com.example.myapplication.entity.SecondNode

class LeafNodeProvider(override val itemViewType: Int, override val layoutId: Int) : BaseNodeProvider() {

    override fun convert(helper: BaseViewHolder, item: BaseNode) {
        if (item is SecondNode) {
            helper.setText(R.id.tv_type, item.type)
            helper.setText(R.id.tv_name, item.name)
        }
    }
}

数据的结构如下:

private val nodes = arrayListOf(
    FirstNode("树枝节点", "增加掘力值的方法", arrayListOf(
        SecondNode("树叶节点", "点赞", null),
        SecondNode("树叶节点", "收藏", null),
    SecondNode("树叶节点", "评论", null)
    ))
)

最后通过调用expandOrCollapse()方法来展开和折叠节点条目。

nodeAdapter.setOnItemClickListener { _, _, position ->
    nodeAdapter.expandOrCollapse(
        position
    )
}