Android:RecyclerView封装,打造列表极简加载

前言

mBinding.recycler.linear().divider()
            .set {
                addLayout(R.layout.layout_ordinary_item)
            }.setList(getList())

如果我要说,除了数据和布局之外,以上的几行代码,就实现了一个列表加载,有老铁会相信吗?

可以很负责人的告诉大家,经过Kotlin的DSL语言和扩展函数的使用,实现一个列表加载就是如此的简单,有的老铁可能会很懵,怎么没有看到适配器啊,也没有看到设置布局管理器啊,这就是此次封装的隐秘之处,弱化adapter的存在,当然了只是使用上弱化,其内部依然用到了adapter。

经过代码上不断的完善,功能上不断的拓展,目前的封装,已经满足绝大部分的常见需求,决定开源出来,希望可以给RecyclerView的使用上,带来一丝变化,当然了,此项目也会不定期更新,也欢迎大家在使用上提出自己的见解和问题,也希望点个小星星。

目前已开发功能:

Android:RecyclerView封装,打造列表极简加载_第1张图片

本篇文章的概述如下

1、封装库快速使用

2、具体功能一一概述

3、开源地址

4、使用总结

5、后续规划

一、封装库快速使用

目前封装的库,已经上传到远程Maven,大家可以按照如下的步骤进行使用:

1、在你的根项目下的build.gradle文件下,引入maven。

allprojects {
    repositories {
        maven { url "https://gitee.com/AbnerAndroid/almighty/raw/master" }
    }
}

2、在你需要使用的Module中build.gradle文件下,引入依赖。

dependencies {
    implementation 'com.vip:relist:1.0.5'//一个包含了列表加载和下拉刷新、上拉加载的库,它包含了下面的两个库,使用它,下面的两个就不要引用了。
    implementation 'com.vip:list:1.0.4'//列表加载库,如果使用了relist,这个不要再引用
    implementation 'com.vip:refresh:1.0.0'//下拉刷新、上拉加载库,如果使用了relist,这个不要再引用
}

需要注意的是,目前拆分了三个依赖,大家一定看清楚后,进行选择使用。refresh依赖只是对SmartRefreshLayout包了一层,没有做过多的扩展,如果大家项目中已经有了刷新库,其实只用list这个依赖即可。list库是纯RecyclerView封装库,没有用到任何的第三方,大家可以放心使用。

依赖

概述

版本号

集成

relist

一个包含了列表加载和下拉刷新、上拉加载的库

1.0.5

implementation 'com.vip:relist:1.0.5'

list

只包含列表加载(添加头尾、缺省页、侧滑删除、吸顶效果、分割线、DataBinding等)

1.0.4

implementation 'com.vip:list:1.0.4'

refresh

只包含下拉刷新、上拉加载

1.0.0

implementation 'com.vip:refresh:1.0.0'

二、具体功能使用

关于功能上的使用,大家可以直接看源码,或者访问github地址后看使用说明也可以,当然了,在这里我也罗列一下。

1、普通的列表加载

普通的列表加载,就是RecyclerView原始的用法,创建适配器Adapter,给RecyclerView设置布局管理器,给RecyclerView设置适配器,虽然说,Adapter已经抽取封装了,但考虑到还有一部分小伙伴具有怀旧意识,也很喜欢使用这种方式加载列表,于是呢,这种方式就保留了。

具体使用,举例如下:

创建适配器

即便是普通列表加载,这里也是建议继承父类BaseAdapter,因为直接继承RecyclerView.Adapter的方式, 还得要重写一大堆方法,实在是冗余。

BaseAdapter对RecyclerView.Adapter做了一层封装,只需要传递要加载的layout和数据对象泛型即可,这两个就不贴源码了,一个是xml布局,一个自己定义的对象,没啥好说的。

数据绑定和逻辑处理,实现dataOperation方法即可,当然,这个也是强制要实现的方法。


class OrdinaryListAdapter : BaseAdapter(R.layout.layout_ordinary_item) {

    override fun dataOperation(holder: BaseViewHolder, t: OrdinaryListBean?, position: Int) {

        t?.title?.let {
            holder.setText(R.id.tv_title, it)
        }

        t?.desc?.let {
            holder.setText(R.id.tv_desc, it)
        }

        val ivPic = holder.findView(R.id.iv_pic)

        t?.icon?.let {
            ivPic.setImageResource(it)
        }


    }
}

设置布局管理器和适配器

这些代码就比较熟悉了吧,都是RecyclerView的原始用法,点击事件调用adapter中的setOnItemClickListener方法即可。

因为使用了ViewDataBinding,这里的mBinding.recycler指的是RecyclerView控件。

       val manger = LinearLayoutManager(requireContext())
        manger.orientation = LinearLayoutManager.VERTICAL
        //设置布局管理器
        mBinding.recycler.layoutManager = manger

        val adapter = OrdinaryListAdapter()
        //设置适配器
        mBinding.recycler.adapter = adapter

        //设置分割线
        mBinding.recycler.addItemDecoration(
            ItemDivider(
                Color.parseColor("#cccccc"),
                RecyclerView.VERTICAL, 0
            )
        )

        //设置数据
        adapter.setList(getList())

        adapter.setOnItemClickListener {
            //条目点击事件
            Toast.makeText(requireContext(), "当前点击条目为:$it", Toast.LENGTH_SHORT).show()
        }

简化设置布局管理器和适配器

看到上面的代码,还是觉得有些冗余,普通列表加载,也是建议大家尽量使用简洁用法。

 val adapter = OrdinaryListAdapter()

  mBinding.recycler.linear()//设置布局管理器
            .divider()//设置分割线
            .adapter=adapter//设置适配器

       //设置数据
        adapter.setList(getList())

        adapter.setOnItemClickListener {
            //条目点击事件
            Toast.makeText(requireContext(), "当前点击条目为:$it", Toast.LENGTH_SHORT).show()
        }
 

布局管理器调用如下:

垂直列表:linear()
横向列表:linear(RecyclerView.HORIZONTAL)
网格列表:grid(2),一列展示几个,传递数值即可
瀑布流列表:staggered(2),一列展示几个,传递数值即可

至于其他的列表展示,如,网格和瀑布流形式,只需要更改布局管理器即可。

2、封装之后的列表加载

封装之后的列表加载,是推荐使用的,相对于普通的列表加载,完全弱化了适配器的存在,只需要考虑数据处理即可,非常的简单。

mBinding.recycler.linear()
            .divider()
            .set {
                addLayout(R.layout.layout_ordinary_item)
                bindData {
                    //获取DataBinding
                    val binding = getDataBinding()
                    //获取Model
                    val model = getModel(adapterPosition)
                    binding?.apply {
                        tvTitle.text = model.title
                        tvDesc.text = model.desc
                        ivPic.setImageResource(model.icon!!)
                    }
                    setOnItemClickListener {
                        //条目点击事件
                        Toast.makeText(requireContext(), "当前点击条目为:$it", Toast.LENGTH_SHORT)
                            .show()
                    }
                }

            }.setList(getList())

相关方法说明:

属性或方法

概述

mBinding.recycler

因为使用了ViewDataBinding,这里的mBinding.recycler指的是RecyclerView控件。

linear

布局管理器,可调用方法如下:

垂直列表:linear()

横向列表:linear(RecyclerView.HORIZONTAL)

网格列表:grid(2),一列展示几个,传递数值即可

瀑布流列表:staggered(2),一列展示几个,传递数值即可

divider

分割线

set

扩展函数,泛型为加载的对象

addLayout

添加列表Item展示的布局

bindData

绑定数据和处理逻辑

getDataBinding

获取ViewDataBinding

getModel

获取数据对象

setOnItemClickListener

条目点击事件

setList

设置列表数据

有的老铁可能会问了,封装之后简单是简单了,但是如果遇到复杂列表,都写到一个类里,代码量实在是太多了,哎!这确实是 个问题,但是呢,又不是问题,如果bindData里的逻辑比较多,你完成可以抽取到其他地方,比如ViewModel里,在ViewModel定义 方法后,调用即可,又或者呢,使用后边的DataBinding。

3、多条目列表加载

Android:RecyclerView封装,打造列表极简加载_第2张图片

效果图可以忽略,重在功能哈~,多条目加载也封装了,有适配器和无适配方式,在实际的业务开发中,大家可以选择性进行使用。

有适配器模式

也就是和传统的多条目保持一致,都在适配器里进行数据的渲染和逻辑处理。

还是那句话,多条目的适配器也抽取了基类,既然都有基类了,为了代码上的简洁,建议还是继承基类比较好。

如何添加多条目?

直接在构造方法里,调用addLayout即可,有几个多条目就添加几个,是不是非常的方便,泛型为数据对象,参数为多条目xml布局。

如何数据渲染和逻辑处理?

实现bindOperation方法即可,通过holder.itemViewType来却分多条目的类型,当然了这个必须和数据对象里的类型保持一致。

区分多条目类型

数据对象实现BaseMultipleItem,重写itemViewType属性,itemViewType就是用来区分多条目类型的,可以随意设置,或者是接口的某个参数,或者是对应的layout。

class OrdinaryMultipleItemAdapter : BaseMultipleItemAdapter {

    constructor() {
        //添加多条目类型以及绑定的数据对象
        addLayout(R.layout.layout_ordinary_multiple_01)
        addLayout(R.layout.layout_ordinary_multiple_02)
        addLayout(R.layout.layout_ordinary_multiple_03)
    }

    override fun bindOperation(holder: BaseViewHolder, t: BaseMultipleItem?, position: Int) {

        when (holder.itemViewType) {
            1 -> {
                val bean = t as MultipleItem01Bean
                holder.setText(R.id.tv_title, bean.title!!)
                holder.setText(R.id.tv_desc, bean.desc!!)
                val ivPic = holder.findView(R.id.iv_pic)
                ivPic.setImageDrawable(bean.icon)
            }
            2 -> {
                val bean = t as MultipleItem02Bean
                val ivPic01 = holder.findView(R.id.iv_01)
                val ivPic02 = holder.findView(R.id.iv_02)
                val ivPic03 = holder.findView(R.id.iv_03)

                ivPic01.setImageDrawable(bean.icon01)
                ivPic02.setImageDrawable(bean.icon02)
                ivPic03.setImageDrawable(bean.icon03)
            }
            3 -> {
                val bean = t as MultipleItem03Bean
                holder.setText(R.id.tv_content, bean.content!!)
            }
        }
    }

}

设置布局管理器和适配器

val adapter = OrdinaryMultipleItemAdapter()
        mBinding.recycler.linear()
            .divider().adapter = adapter
        adapter.setList(getMoreList())

无适配器模式

无适配器模式,更加的简单,只需要调用setMore方法即可,有多少条目就调用addLayout几次,在bindData里进行数据渲染和逻辑处理,当然了,也可以使用DataBinding形式,需要追加BR。

mBinding.recycler.linear()
            .divider()
            .setMore {
                addLayout(R.layout.layout_multiple_01, BR.multiple1)
                addLayout(R.layout.layout_multiple_02, BR.multiple2)
                addLayout(R.layout.layout_multiple_03, BR.multiple3)
                bindData {

                }
            }.setList(getMoreList())

4、DataBinding形式列表加载

Android:RecyclerView封装,打造列表极简加载_第3张图片

DataBinding的出现,使得数据绑定更加的简单化,大大减少了代码的书写,大家可以采用提供的两种方式进行使用,一种是针对Item的绑定,一种是对RecyclerView自身的绑定。

Item绑定

第一步,addLayout,增加和xml绑定的BR

 mBinding.recycler.linear().divider()
            .set {
                addLayout(R.layout.layout_ordinary_bind_item, BR.ordinary)
                setOnItemViewClickListener { view, position ->
                    //条目点击事件
                    Toast.makeText(requireContext(), "当前点击条目为:$position", Toast.LENGTH_SHORT).show()
                }
            }.setList(getList())

第二步,xml数据绑定




    

        
    

    

        

        

            

            

        

    

RecyclerView绑定

第一步,xml中RecyclerView数据绑定




    

        
    

    

        

    

第二步、对应model中提供数据

class RecyclerViewBindViewModel : BaseViewModel() {

    val layoutId = R.layout.layout_ordinary_bind_item//item列表

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:获取视图绑定的name
     */
    fun getListVariableName(): Int {
        return BR.ordinary
    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:模拟数据
     */
    fun getList(): MutableList {

        return mutableListOf().apply {
            for (a in 0..20) {
                val bean = OrdinaryListBean()
                bean.title = "我是标题$a"
                bean.desc = "我是描述信息$a"
                bean.icon = R.mipmap.vip_list_logo
                add(bean)
            }
        }
    }
}

属性一览

属性

类型

概述

listManager

Int

布局管理器:默认为纵向的普通列表。

0:普通列表

1:网格

2:瀑布流

listOrientation

Int

设置列表方向,默认纵向。

横向:RecyclerView.HORIZONTAL

listSpanCount

Int

展示几列,适用于网格和瀑布流

isDivider

Boolean

是否展示分割线

dividerDrawable

Int

分割线样式

listLayout

Int

Item布局

listData

MutableList

数据

listVariableName

Int

绑定的BR

listAdapter

OnAdapterListener

返回适配器,可以通过这里实现,适配器中的逻辑处理

isMultiple

Boolean

是否是多条目

multipleAdapter

OnAdapterMultipleListener

返回多条目适配器

multipleData

MutableList

多条目数据

multipleLayout

MutableList

多条目布局

multipleLayoutBindData

MutableList>

layout绑定的数据对象

multipleVariableName

MutableList

xml绑定的对应的VariableName

两种绑定方式,各有优点,大家可以根据实际的业务进行选择使用。

5、设置分割线

Android:RecyclerView封装,打造列表极简加载_第4张图片

调用divider方法即可。

 mBinding.recycler.linear()
            .divider()
            .set {
                addLayout(R.layout.layout_ordinary_bind_item, BR.ordinary)
            }.setList(getList())

divider可选参数如下:

参数

类型

概述

color

Int

分割线颜色,默认是#cccccc

orientation

Int

分割线方向,默认和纵向保持一致,RecyclerView.VERTICAL

lineWidth

Int

分割线的高度

margin

Int

分割线距离左右或者距离上下的边距

hideLast

Boolean

是否隐藏最后一条分割线

itemType

Int

默认为0,不为0时,则绘制横向线条,适用于网格列表分割线

6、头和尾追加和删除

头和尾的添加支持layout和View两种添加方式,代码如下所示:

mAdapter?.addHead(R.layout.layout_head)//初始 添加头  不用刷新
mAdapter?.addFoot(R.layout.layout_foot)//初始 添加尾  不用刷新

后续如果动态添加头尾,需要更新适配器。

 mAdapter?.addHead(view, true)//追加头,需要刷新
 mAdapter?.addFoot(view, true)//追加尾,需要刷新

动态删除头尾操作,支持按照索引进行删除。

mAdapter?.removeHead()
mAdapter?.removeFooter()

7、数据追加和删除

初始添加数据

mAdapter?.setList(getList())

追加数据,支持对象和集合两种方式

 mAdapter?.addData()

删除数据

mAdapter?.removeData(0)

8、设置缺省页面

缺省页面没什么好说的,数据为空或者数据加载错误的时候,设置一张占位View。

空页面,调用addEmptyView即可,支持layout和View两种模式,错误页面,调用addErrorView,和空页面使用方式一致。

 mAdapter = mBinding.recycler.linear()
            .divider()
            .set {
                addEmptyView(R.layout.layout_empty)//初始化 空页面
                addErrorView(R.layout.layout_error)//初始化  错误页面
                addLayout(R.layout.layout_item, BR.str)
            }

        //初始添加数据
        mAdapter?.setList(getList())

功能

实现

显示空

mAdapter?.showEmptyView()

隐藏空

mAdapter?.hintEmptyView()

显示错误

mAdapter?.showErrorView()

隐藏错误

mAdapter?.hintErrorView()

9、拖拽排序功能

Android:RecyclerView封装,打造列表极简加载_第5张图片

调用drag方法即可实现拖拽排序功能。

 mBinding.recycler.linear()
            .drag()//支持拖拽
            .set {
                addLayout(R.layout.layout_drag_item, BR.drag)
            }.setList(getList())

注意事项

传递的数据对象必须实现BaseNoDragBean,需要重写isDrag属性,false为禁止拖拽,true为允许拖拽。

class DragBean : BaseNoDragBean {
    override var isDrag = false

    var content = ""

}

10、侧滑删除条目

调用slideDelete方法即可,支持左右侧滑删除,传递不同的值即可。

 mBinding.recycler
            .linear()
            .slideDelete()//支持侧滑删除 默认是左滑删除 0是右边  1是左右两边
            .set {
                addLayout(R.layout.layout_main_item, BR.str)
            }.setList(getList())

11、侧滑显示按钮

Android:RecyclerView封装,打造列表极简加载_第6张图片

由原来的set方法改为setSlide方法即可。

 mBinding.recycler
            .linear()
            .divider()
            .setSlide {//如果要显示按钮 使用 setSlide
                addLayout(R.layout.layout_item)
                bindData {
                    val model = getModel(adapterPosition)
                    setText(R.id.tv_content, model)
                }
            }.setList(getList())

有的老铁可能会问,我想展示多个按钮,或者展示自定义的View,如何实现呢?在setSlide调用addSlideLayout,传入自己的xml布局即可。

12、条目吸顶功能

Android:RecyclerView封装,打造列表极简加载_第7张图片

实现吸顶就调用stick方法即可。

 mBinding.recycler.linear()
            .stick()
            .set {
                addLayout(R.layout.layout_stick_item, BR.stick)
            }.setList(getList())

需要注意,数据对象需要实现StickHeaderBean,重写分组标识。

class StickHeaderBean : BaseStickHeaderBean {

    override var stickGroup: String = ""//分组标识

    var name = ""//普通内容
}

13、单选、多选、全选、反选

当然了这都是业务层的逻辑,按理来说,没必要在封装,但是考虑到代码的简洁性,单选和多选的判断逻辑就封装了一下,大家如有用到此功能,可按照如下的方式进行操作即可。

单选

主要的就两部分:

1、开启单选刷新

mNotifySingleChoice = true

2、判断单选

 adapterPosition == mCheckPosition,可以利用mCheckPosition来判断,进而更新UI。

mBinding.recycler.linear().divider().set {
            mNotifySingleChoice = true//开启单选刷新
            addLayout(R.layout.layout_single_choice_list_item, BR.single)
            bindData {
                val binding = this.getDataBinding()
                //判断单选,直接判断  adapterPosition == mCheckPosition 即可
                binding?.checkbox?.isChecked = adapterPosition == mCheckPosition
                setOnItemViewClickListener { view, position ->
                    //条目点击
                    val singleBean = getList()[position]//单选 选择的对象
                    mViewModel.name.set(singleBean.name)
                }
            }
        }.setList(getList())

多选

和单选一样,也是两部分

1、设置多选更新

mNotifyMultipleChoice = true

2、多选回调监听

setOnMultipleChoiceListener{}

mBinding.recycler.linear().divider().set {
            mNotifyMultipleChoice = true//多选更新
            addLayout(R.layout.layout_multiple_choice_list_item, BR.multiple)
            bindData {
                //多选回调监听
                setOnMultipleChoiceListener {
                    var allPrice = 0.0f
                    it.forEach {
                        allPrice += it.price
                    }
                    mViewModel.commodityNumber.set("选择商品数量为:" + it.size)
                    mViewModel.allPrice.set("总价格为:$allPrice")
                }
            }
        }.setList(getList())

14、上拉刷新和下拉加载

Android:RecyclerView封装,打造列表极简加载_第8张图片

刷新和加载使用的是SmartRefreshLayout这个开源框架,毕竟已经有很优秀的框架了,没必要再重新封装一个,具体的用法,大家可以按照SmartRefreshLayout的文档去操作使用就行。

支持全局设置下拉和上拉展示View,只需要在Application里初始化即可。

     //上拉加载和下拉刷新,初始化头和尾
        ListConfig.apply {
            addRefreshHeader {
                ClassicsHeader(it)
            }
            addRefreshFooter {
                ClassicsFooter(it)
            }
        }

这里也简单举个例子:

1、xml中引入

  

        
    

2、代码简单使用

mSmartRefreshLayout?.setOnRefreshLoadMoreListener(object : OnRefreshLoadMoreListener {
            override fun onRefresh(refreshLayout: RefreshLayout) {
               
            }

            override fun onLoadMore(refreshLayout: RefreshLayout) {
             
            }
        })

当然了,为了更好的拓展使用,针对SmartRefreshLayout,又简单做了一层包装,别无他意,就是为了让使用更加的简单。

1、xml里引入PageRefreshLayout

 

2、代码里使用

  mBinding.refresh.getRecycler().linear()
            .divider()
            .set {
                addLayout(R.layout.layout_item, BR.str)
            }
        //刷新和加载
        mBinding.refresh.refresh { isRefresh, refreshLayout ->
            mViewModel.doHttp {
                addData(it)
            }
        }.autoRefresh()

直接调用refresh方法即可,isRefresh为true是下拉,否则就是上拉,非常的方便,而addData方法,则实现了分页加载数据,上拉和下拉直接调用addData即可。

PageRefreshLayout可调用方法如下

方法

参数

概述

autoRefresh

无参

自动刷新操作

refresh

无参

静默刷新(不带下拉刷新动画)

addData

Collection

添加数据,分页会自动追加数据,下拉和上拉会自动关闭

setEnableRefresh

Boolean

是否禁止下拉

setEnableLoadMore

Boolean

是否禁止上拉

finishRefresh

无参

关闭下拉刷新

finishLoadMore

无参

关闭上拉刷新

getPager

无参

获取当前页码

refresh

回调函数

刷新和加载方法

getSmartRefresh

无参

获取SmartRefreshLayout

getRecycler

无参

获取RecyclerView

addEmptyView

Int/View

添加空的布局,支持layout和View

addErrorView

Int/View

添加错误的布局,支持layout和View

showEmptyView

无参

显示空布局

showErrorView

无参

显示错误布局

hintEmptyView

无参

隐藏空布局

hintErrorView

无参

隐藏错误布局

setHeightWrapContent

无参

设置整体的列表由充满改为包裹内容。

三、开源地址

关于大家在使用上的问题以及后续的优化,或者功能新增,都会时长更新,方便的话,给个小星星呗~

开源地址:https://github.com/AbnerMing888/VipList

四、使用总结

1、无适配器的模式,在逻辑相对复杂的页面,建议大家可以抽取到ViewModel中实现,当然,也可以采用DataBinding的形式。

2、很多使用方式,或者常见的业务开发场景,在文档中或者源码中,都会详细的备注,大家可以细心的查看即可。

3、条目点击事件,给出了两个,大家可以选择性使用。

只返回索引

setOnItemClickListener { 
                    
}

返回当前点击的View和索引

 setOnItemViewClickListener { view, position ->
                   
 }

 

五、后续规划

此库的封装,除了刷新加载库使用了SmartRefreshLayout,其他的都是自己从0到1的开发,目前,自己已经在项目中使用,暂时没有出现任何问题,当然了,后续,也会不断的对其进行优化,增加一些其他的功能,希望有需要的小伙伴,长期关注。

关于维护上,不断的优化和解决一些大家所提的问题。

关于源码上,后续会逐一剖析其实现方式,确确实实,里面用到了一些另类技术,当然了,这都是后话了。

你可能感兴趣的:(RecyclerView使用,RecyclerView封装,Android列表滑动删除,Android拖拽排序,RecyclerView添加头)