【Android】BaseQuickAdapter使用(RecyclerView万能适配器)

前言

RecyclerView是Android用于取代ListView的SDK,它的灵活性和可替代性都比ListView更好,但RecyclerView也存在一些问题: 高度不能自适应、最后一个 Item 显示不全被遮挡等。而无论ListView还是RecyclerView都必不可少地会使用到适配器,其配置比较繁琐。

BaseQuickAdapter 可以对繁琐的适配器进行快速构建,相比原始的RecyclerView.Adapter适配器,能减少70%以上的代码。

本文将使用Kotlin语言来编写,Kotlin作为安卓板块的新语言,集大家之所长,一定程度上提高了开发效率,后续会有介绍。


BRVAH官方使用指南:BRVAH官方使用指南


一、导入依赖

在 build.gradle(Module:app) 的 dependencies 添加:

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

二、适配器比较

  1. 原始的 RecyclerView.Adapter
class RecyclerAdapter(private val context: Context, private val data: List<String>)
    : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    override fun getItemCount(): Int = data.size
    
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder?{
        return MyRecyclerViewHolder(LayoutInflater.from(context)
                    .inflate(R.layout.item_recycler, parent, false))

    }

    //override fun getItemViewType(position: Int): Int{}

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        holder.textView.text = ""
        holder.textView.setOnClickListener {}
    }

    inner class MyRecyclerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var textView: TextView = view.findViewById(R.id.item_tv)
    }
}

原始的 RecyclerView.Adapter 需要编写4~5个方法

1、getItemCount
2、onCreateViewHolder
3、getItemViewType
4、onBindViewHolder
5、MyRecyclerViewHolder自定义ViewHolder

  1. BaseQuickAdapter适配器
class MyAdapter(private val layoutRes: Int) :
    BaseQuickAdapter<String, BaseViewHolder>(layoutRes) {
    override fun convert(holder: BaseViewHolder, item: String) {
        holder.setText(R.id.textView, item)
    }
}

只需要继承BaseQuickAdapter<T, VH>,第一个泛型对应数据类型ItemBean,第二个泛型对应ViewHolder,一般直接填写BaseViewHolder。

然后在convert方法(类似onBindViewHolder方法)中绑定数据、设置事件等,这样只需要编写一个方法即可完成大量的适配工作。

三、基础使用

  1. 添加到 RecycleView
    val layoutManager = LinearLayoutManager(this)
    recyclerView.layoutManager = layoutManager
    val adapter=MyAdapter(R.layout.item_recycler)
    recyclerView.adapter = adapter
  1. 赋值并刷新
    adapter.setNewInstance(data) //或者setNewData(),初始化数据
    adapter.setList(data) //更新数据
  1. holder常用方法
    holder.getView(viewId) //获取控件
    holder.setText(R.id.textView, str) //设置文本
    holder.getView(viewId).setOnClickListener {} //指定控件的点击事件
    holder.getLayoutPosition() //获取当前item的position
  1. 外部添加点击事件
    //item整体点击事件
    adapter.setOnItemClickListener { adapter, view, position ->
        onItemClick(view,position)
    }
    //注册item对应子控件
    adapter.addChildClickViewIds(R.id.item_tv) 
    //item子控件点击事件
    adapter.setOnItemChildClickListener { adapter, view, position ->
        onItemChildClick(view,position)
    }

  1. 添加动画
    adapter.setAnimationWithDefault(type)
    //默认的AnimationType类型:AlphaIn, ScaleIn, SlideInBottom, SlideInLeft, SlideInRight
  1. 添加头部、尾部、空布局
    //添加头部、尾部、空布局
    val footView: View =
        LayoutInflater.from(this).inflate(R.layout.layout_foot, recyclerView, false)
    adapter.setFooterView(footView)
    val headView: View =
        LayoutInflater.from(this).inflate(R.layout.layout_head, recyclerView, false)
    adapter.setHeaderView(headView)
    val emptyView: View =
        LayoutInflater.from(this).inflate(R.layout.layout_empty, recyclerView, false)
    adapter.setEmptyView(emptyView)
  1. 刷新加载相关
    //打开或关闭上拉加载
    adapter.setEnableLoadMore(boolean)
    // 滑动最后一个Item的时候回调onLoadMoreRequested方法
    adapter.setOnLoadMoreListener(requestLoadMoreListener)
    //加载完成
    adapter.loadMoreComplete();
    //加载失败
    adapter.loadMoreFail();
    //加载结束
    adapter.loadMoreEnd();
    //设置自定义加载布局
    adapter.setLoadMoreView(CustomLoadMoreView())
    //如果上拉结束后,下拉刷新需要再次开启上拉监听,需要使用setNewData方法填充数据

    //--------------------
    
    //打开或关闭下拉刷新
    adapter.setUpFetchEnable(true)
    //设置刷新监听
    adapter.setUpFetchListener(BaseQuickAdapter.UpFetchListener(){})
    //设置开始加载的位置
    adapter.setStartUpFetchPosition(2)
  1. 添加拖拽、滑动删除
    val adapter = ItemDragAdapter(mData) // adapter 需要继承 BaseItemDraggableAdapter
    val itemDragAndSwipeCallback = ItemDragAndSwipeCallback(adapter) //拖拽和删除回调
    val itemTouchHelper = ItemTouchHelper(itemDragAndSwipeCallback) 
    itemTouchHelper.attachToRecyclerView(recyclerView)  //绑定 recycleView
    
    // 开启拖拽
    adapter.enableDragItem(itemTouchHelper, R.id.item_tv, true)
    adapter.setOnItemDragListener(onItemDragListener) //实现 OnItemDragListener 接口
    
    // 开启滑动删除
    adapter.enableSwipeItem()
    adapter.setOnItemSwipeListener(onItemSwipeListener) //实现 OnItemSwipeListener 接口

四、多布局和分组布局

  • 分组布局继承类:

BaseSectionQuickAdapter:快速实现带头部的适配器

  • 多布局继承类:根据需求选择

BaseMultiItemQuickAdapter:适用于类型较少,业务不复杂的场景,便于快速使用
BaseDelegateMultiAdapter:数据类型任意,代理类方式,适用于实体类不方便拓展
BaseProviderMultiAdapter:数据类型任意,不限定ViewHolder类型,单独的自定义ViewHolder类,避免在convert方法中做大量的业务逻辑

1.BaseMultiItemQuickAdapter

数据类必须实现MultiItemEntity接口,需要给每一个数据设置itemType。

class BaseMultiQuickItem(override val itemType: Int, val data: BaseQuickerItem) : MultiItemEntity {
     companion object {
          const val FIRST_TYPE = 1
          const val SECOND_TYPE = 2
     }
}

适配器重写getItemType方法来返回不同的数据类型。

inner class BaseQuickInfoListAdapter :
     BaseMultiItemQuickAdapter<BaseMultiQuickItem, BaseViewHolder>() {
     
     init {
         addItemType(BaseMultiQuickItem.FIRST_TYPE, R.layout.layout_first_item)
         addItemType(BaseMultiQuickItem.SECOND_TYPE , R.layout.layout_second_item)
     }
    
     override fun convert(holder: BaseViewHolder, itemMulti: BaseMultiQuickItem) {
         when(holder.itemViewType){
             BaseMultiQuickItem.FIRST_TYPE  -> {
                 holder.setText(R.id.item_tv, "布局一")
             }
             BaseMultiQuickItem.SECOND_TYPE  -> {
                 holder.setText(R.id.item_tv, "布局二")
             }
         }
     }
    }

2.BaseDelegateMultiAdapter

使用代理类的方式,返回布局id和item类型,数据类依然使用 BaseMultiQuickItem。

class DelegateMultiAdapter :
    BaseDelegateMultiAdapter<BaseMultiQuickItem?, BaseViewHolder>() {

    override fun convert(helper: BaseViewHolder, item: BaseMultiQuickItem?) {
        when (helper.itemViewType) {
            BaseMultiQuickItem.FIRST_TYPE -> helper.setText(R.id.item_tv,"布局一")
            BaseMultiQuickItem.SECOND_TYPE -> {
                when (helper.layoutPosition % 2) {
                    0 -> helper.setText(R.id.item_tv, "布局二 第一种")
                    1 -> helper.setText(R.id.item_tv, "布局二 第二种")
                    else -> {}
                }
            }
            else -> {}
        }
    }
    
    init {
        //设置代理,通过内部类返回类型
        setMultiTypeDelegate(object : BaseMultiTypeDelegate<BaseMultiQuickItem?>() {
            override fun getItemType(data: List<BaseMultiQuickItem?>, position: Int):Int {
                when (position % 3) {
                    0 -> return BaseMultiQuickItem.FIRST_TYPE
                    1 -> return BaseMultiQuickItem.SECOND_TYPE
                    else -> {}
                }
                return 0
            }
        })
        //将类型和布局绑定
        getMultiTypeDelegate()
            ?.addItemType(BaseMultiQuickItem.FIRST_TYPE, R.layout.layout_base_quick_first_item)
            ?.addItemType(BaseMultiQuickItem.SECOND_TYPE, R.layout.layout_base_quick_second_item)
    }
}

3.BaseProviderMultiAdapter

当有多种条目时,避免在convert方法中做大量的业务逻辑,将每种Item都分离成一个个单独的类,然后整合到适配器中,最大化自定义数据类型。

class ProviderMultiAdapter : BaseProviderMultiAdapter<BaseMultiQuickItem?>() {

    override fun getItemType(data: List<BaseMultiQuickItem?>, position: Int): Int {
        when (position % 3) {
            0 -> return BaseMultiQuickItem.FIRST_TYPE
            1 -> return BaseMultiQuickItem.SECOND_TYPE
            else -> {}
        }
        return 0
    }
    
    init {
        // 注册 Provider
        addItemProvider(FirstItemProvider())
        addItemProvider(SecondItemProvider())
    }
    }

把大量的业务逻辑抽取到了对应的ItemProvider中。

class FirstItemProvider : BaseItemProvider<BaseMultiQuickItem?>() {

    override val itemViewType: Int
        get() = BaseMultiQuickItem.FIRST_TYPE

    override val layoutId: Int
        get() = R.layout.layout_first_item

    //可返回自定义的 ViewHolder
    override fun onCreateViewHolder(parent: ViewGroup, viewType:Int): BaseViewHolder {
        return super.onCreateViewHolder(parent,viewType)
    }

    override fun convert(helper: BaseViewHolder, data: BaseMultiQuickItem?) {
        if (helper.absoluteAdapterPosition % 2 === 0) {
            helper.setText(R.id.item_tv, "")
        } else {
            helper.setText(R.id.item_tv, "")
        }
    }

    override fun onClick(
        helper: BaseViewHolder,
        view: View,
        data: BaseMultiQuickItem?,
        position: Int
    ) {}
}

4.BaseSectionQuickAdapter

数据类需继承SectionEntity抽象类。

class BaseSectionQuickItem(
     override val isHeader: Boolean, 
     bean: BaseQuickerItem
) : SectionEntity<String>()

适配器:

class SectionQuickAdapter(layoutResId: Int, sectionHeadResId: Int, data: MutableList<BaseSectionQuickItem>?) :
    BaseSectionQuickAdapter<BaseSectionQuickItem, BaseViewHolder>(layoutResId, sectionHeadResId, data) {
    override fun convert(helper: BaseViewHolder, item: BaseSectionQuickItem) {
    }
    
    override fun convertHeader(helper: BaseViewHolder, item: BaseSectionQuickItem) {
    }
}

总结

BaseQuickAdapter功能强大,可以实现多布局、上拉刷新、下拉加载、侧滑删除、甚至树状列表等功能,且方便快捷,如果想要提高开发效率,适配器优先推荐使用它。这里可能介绍不全,详细的可参考官方文档。

五一假期的第一天,被迫码字和学习😭

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

推荐阅读更多精彩内容