YasuoRecyclerViewAdapter!让你在Android中快乐的实现列表!

图片来自:https://www.zcool.com.cn/work/ZNDU0NzA2MTY=.html

一、前言

自从我开始做安卓开发以来,我就得了一种病,Adapter编写焦虑症。在现如今的安卓App开发中,大家用得最多的ViewGroup,那一定是身为老大哥的RecyclerView,单布局列表,多布局列表,网格列表,瀑布流列表,折叠列表,吸顶列表,甚至一个无需滚动的页面,出于屏幕高度适配考虑,都有可能做成列表形式。这足以证明RecyclerView在实际开发中的重要性。

那么这样一个如此重要的组件,通常情况下我们需要怎么实现呢?

第一步:在布局中添加RecyclerVIew
第二步:创建item布局及其实体类
第三步:继承RecyclerView.Adapter类,编写自定义Adapter
第四步:绑定Adapter到RecyclerView

以上便是实现一个完整的列表所需的步骤,其中第三步可谓是整个流程中最为复杂的,目前的一些三方库,在一定程度上简化了第三步,让开发者在继承了他们自定义的Adapter之后,可以少些很大一部分的代码,从而提升开发效率,我以前也是用过这些优秀的库,比如安卓界使用最广泛BRVAH,为开发者们大大减少了开发时间,让大家有时间回家陪陪家人,首先在这里感谢各位大佬的贡献。

但是,由于我接触kotlin比较早,2016年7月开始从事Android开发,2017年初开始接触kotlin,然后在17年5月google开始正式钦定kotlin之后便开始正式使用kotlin做项目,从那时起,我就被kotlin的dsl所吸引,便开始尝试将这些优秀的第三方库进行dsl的改造,奈何当时的能力不足,这种几乎重写的改造宣告失败。既然大型改造不行,那我能不能去找一些别人写好的adapter dsl呢?于是经过我的不断搜索,终于在github上找到了一些库,比如kotlin-adapterYasha

这两个都是优秀的库,曾经我也在项目中使用过他们,但是渐渐地我发现他们已经无法满足我的一些需求了,我便开始尝试写一个自己用的库。当时这个库还是存在于我的私有项目中,我在项目中遇到的问题就可以及时改,直到19年的上半年为止,这个库基本可以用于大多数日常开发,当时我就考虑将它进行一些完善工作后开源,但是世事难料,由于工作原因,19年的7月,我去了一趟柬埔寨,没错,就是程序员们口诛笔伐的东南亚国家之一。

去那边之后,因为人手问题,我不得不学习了vue开发,flutter开发,以及后端开发,几乎没有时间来搞这个库的开源工作。时间到了20年的9月,由于工作安排,又回到了重庆,又经过几个月的忙碌后,终于在最近抽出了一些时间,来继续进行作业。

当我重新审视这个库的时候,我发现了很多可以优化的点,这代表我这一年多是在进步,我很高兴,然后花了两三周的空余时间将其优化完善,新增一些功能后,我决定在今天将它发布出来,希望大家能够喜欢。

不好意思,前言有点啰嗦,但依旧不妨碍后续的精彩。

二、正片

简单介绍一下这个库:YasuoRecyclerViewAdapter ,为什么要取名为Yasuo,因为亚索==快乐!这个库就是为了让大家在写代码的时候感受到快乐而存在的!

1、功能特色

①、List,Grid,StaggeredGrid类型的正常布局及多布局

②、空白页/头部/尾部

③、加载更多

④、折叠布局(支持多级折叠)

⑤、拖拽、横向滑动删除

⑥、附送两个ItemDecoration,可根据不同需求选择

⑦、采用ObservableList作为数据源,无需手动notify

⑧、支持findViewById,ViewBinding,DataBinding三种模式,可根据你现有项目模式或喜好随意更换!

⑨、动画的高可配置(综合考虑后采用recyclerView的itemAnimator方案,如有需要请自行依赖mikepenz大神的ItemAnimators库)

⑩、吸顶(采用sticky-layoutmanager的方案,低耦合adapter和item,由于原库的position获取有一些bug,便将其集成到本项目中并修复了bug)

2、依赖,最新版本请看github或者jitpack

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
dependencies {
    implementation 'com.github.q876625596:YasuoRecyclerViewAdapter:x.y.z'
}

2、示例展示

如果想直接Ctrl CV代码,快速上手的同学,请直接移步sample

吸顶.gif

加载更多.gif

空布局,header,footer.gif
折叠.gif
拖拽,侧滑删除.gif

3、详细介绍

1)数据源

必须使用YasuoList或其子类作为数据源,YasuoList继承自ObservableArrayList,新增了部分常用方法,并在adapter内部做了监听处理,因此使用该类型数据源可以不用手动notify

2)简单写法(单布局/多布局/header/footer)
    fun findViewByIdMode(){
        //数据源
        val list = YasuoList<Any>()
        val headerList = YasuoList<Any>()
        val footerList = YasuoList<Any>()
        binding.myRV.layoutManager = GridLayoutManager(this, 3)
        //findViewById模式
        binding.myRV.adapterBinding(this,list){
            //do something
            //绑定文本布局
            //只需要给对应的布局配置holderConfig,即可实现多布局,header,footer
            holderConfig(R.layout.item_layout_text, TextBean::class) {
                onHolderBind { holder, item ->
                    holder.getView<TextView>(R.id.itemText).apply {
                        text = item.text.value
                    }
                }
            }
        }
        //ViewBinding模式
        binding.myRV.adapterViewBinding(this,list){
            //do something
            //配置文本布局
            //只需要给对应的布局配置holderConfig,即可实现多布局,header,footer
            holderConfig(R.layout.item_layout_text, TextBean::class, { ItemLayoutTextBinding.bind(it) }) {
                onHolderBind { holder, item ->
                    itemText.text = item.text.value
                }
            }
        }
        //DataBinding模式
        binding.myRV.adapterDataBinding(this,list){
            //do something
            //配置文本布局
            //只需要给对应的布局配置holderConfig,即可实现多布局,header,footer
            holderConfig(R.layout.item_layout_text_data_binding, TextBean::class, ItemLayoutTextDataBindingBinding::class) {
                onHolderBind { holder ->
                    //dataBInding模式已在xml中绑定了数据,无需手动设置
                }
            }
        }
    }

以上三种模式的差异就只有这么一点,相互切换也相当的方便。

3)空布局

空布局的使用也非常简单,先将空布局的holderConfig配置之后,再调用adapter.showEmptyLayout就行了。

        binding.myRV.adapterViewBinding(this,list){
            //do something
            holderConfig(R.layout.item_layout_text, TextBean::class, { ItemLayoutTextBinding.bind(it) }) {
                onHolderBind { holder, item ->
                    itemText.text = item.text.value
                    itemText.setOnClickListener {
                        showEmptyLayout(/*空布局实体*/EmptyBeanTwo(), /*是否清空header*/true, /*是否清空footer*/true)
                    }
                }
            }
        }
4)对布局设置占比

设置占比有两种方式,第一种,给一种类型的布局设置占比:

        binding.myRV.adapterViewBinding(this,list){
            //do something
            holderConfig(R.layout.item_layout_text, TextBean::class, { ItemLayoutTextBinding.bind(it) }) {
                //do something
                //给某个itemViewType的布局统一设置
                //瀑布流占满一行
                staggeredGridFullSpan = true
                //网格布局占比
                gridSpan = 3
            }
        }

第二种,针对某个item单独设置占比:

        list.add(ImageBean(MutableLiveData(ContextCompat.getDrawable(this@MainActivity, R.drawable.eee))).apply {
                    //给某个item单独设置
                    //瀑布流占满一行
                    staggeredGridFullSpan = true
                    //网格布局占比
                    gridSpan = 3
        }

判断优先级:单个item设置 > 类型设置

5)加载更多

加载更多和空布局类似,也是先将加载更多布局的holderConfig配置之后,再调用adapter.showLoadMoreLayout使空布局显示出来,最后添加adapter.onLoadMoreListener监听即可。

        binding.myRV.adapterViewBinding(this,list){
            //展示加载更多
            showLoadMoreLayout(DefaultLoadMoreItem())
            //设置加载更多的监听
            onLoadMoreListener(binding.myRV) {
                //请求数据...
            }
            //do something
        }
6)拖拽/侧滑删除

只需要使用adapter.enableDragOrSwipe即可启用拖拽,同时也可以设置监听,设置手势方向,以及对某些特定布局禁用等

        binding.myRV.adapterViewBinding(this,list){
            //拖拽/侧滑删除
            enableDragOrSwipe(binding.myRV, isLongPressDragEnable = true, isItemViewSwipeEnable = true)
            //do something
        }
7)吸顶

首先设置layoutManager:StickyLinearLayoutManagerStickyGridLayoutManagerStickyStaggeredGridLayoutManager
吸顶有两种方式,第一种,对某一个类型的布局设置吸顶

        binding.myRV.adapterViewBinding(this,list){
            //do something
            holderConfig(R.layout.item_layout_text, TextBean::class, { ItemLayoutTextBinding.bind(it) }) {
                //给某个itemViewType的布局统一设置
                //吸顶,注意,吸顶会默认占满一行
                sticky = true
                //do something
            }
        }

第二种,针对某个item设置吸顶

list.add(ImageBean(MutableLiveData(ContextCompat.getDrawable(this@MainActivity, R.drawable.eee))).apply {
        //给某个item单独设置
        //吸顶,注意,吸顶会默认占满一行
        sticky = true
}

判断优先级:单个item设置 > 类型设置

8)折叠布局

折叠布局需要数据类继承YasuoFoldItem,之后只需要使用adapter.expandOrFoldItem来展开/收起即可,支持多级折叠,如果需要删除或添加折叠布局中的某个item,建议使用adapter.removeAndFoldListItemadapter.addAndFoldListItem方法

9)动画配置

动画采用mikepenz大神的ItemAnimators库,如有需要,请先自行依赖该库。

        binding.myRV.itemAnimator = SlideLeftAlphaAnimator()
10)附送的itemDecoration

支持为每条边单独设置样式

        binding.myRV.addYasuoDecoration {
            setDecoration(R.layout.item_layout_text, this@MainActivity, defaultRes)
            setDecoration(R.layout.item_layout_image, this@MainActivity, defaultRes)
        }

额外附一个span相等的网格布局专用空白分隔ItemDecoration

        binding.myRV.addItemDecoration(GridSpacingItemDecoration(3, 20, true))

4、api展示

1)adapter可配置属性/方法一览
属性名/方法名 介绍 默认值
itemList 主体列表 YasuoList<T>()
headerList 头部列表 YasuoList<T>()
footerList 尾部列表 YasuoList<T>()
showLoadMoreLayout(loadMoreItem: T) 配置并显示加载更多布局 ------
removeLoadMore() 移除加载更多布局 ------
enableLoadMoreListener() 启用列表滚动到底部时加载更多的监听 ------
disableLoadMoreListener() 禁用列表滚动到底部时加载更多的监听 ------
isShowEmptyLayout() 判断当前是否是显示空布局状态 ------
showEmptyLayout(emptyItem: T, clearHeader: Boolean = false, clearFooter: Boolean = false) 判断当前是否是显示空布局状态 ------
expandOrFoldItem(item: YasuoFoldItem) 展开/折叠某个item ------
removeAndFoldListItem(childItem: Any, foldList: YasuoList<YasuoFoldItem>? = null) 移除一个item的同时移除其折叠列表的相同item ------
getAllListSize() 获取全部列表的长度 ------
getItemListTrueSize() 获取[itemList]的实际长度 ------
getHeaderListTrueSize() 获取[headerList]的实际长度 ------
getFooterListTrueSize() 获取[footerList]的实际长度 ------
getHeaderTruePosition(position: Int) 获取[headerList]的真实position ------
getItemTruePosition(position: Int) 获取[itemList]的真实position ------
getFooterTruePosition(position: Int) 获取[footerList]的真实position ------
inHeaderList(position: Int) 判断position在[headerList]内 ------
inItemList(position: Int) 判断position在[itemList]内 ------
inFooterList(position: Int) 判断position在[footerList]内 ------
setAfterDataChangeListener(listener: () -> Unit) 列表数据发生改变后的监听,在notify之后触发 ------
enableDragOrSwipe(...) 设置item是否可拖拽、滑动删除 ------
onLoadMoreListener(...) 设置加载更多的监听 ------
holderConfig(...) 配置holder,建立数据类与布局文件之间的匹配关系 ------
2)holderConfig可配置属性/方法一览
属性名/方法名 介绍 默认值
sticky 该类型布局是否吸顶 false
isFold 该类型布局是否支持展开折叠 false
gridSpan 该类型布局在grid中的占比 0
staggeredGridFullSpan 该类型布局在staggeredGrid中是否占满 false
holderCreateListener 该类型Holder创建时的监听 null
holderBindListener 该类型的Holder绑定时的监听 null
createBindingFun ViewBinding的创建方法,仅ViewBinding模式 null
variableId xml中对应的数据id,仅DataBinding模式 BR.item
3)YasuoNormalItem可配置属性/方法一览
属性名/方法名 介绍 默认值
sticky 该item是否吸顶 false
gridSpan 该item在grid中的占比 0
staggeredGridFullSpan 该item在staggeredGrid中是否占满 false
4)YasuoNormalItem可配置属性/方法一览
属性名/方法名 介绍 默认值
list 下一级列表 YasuoList<YasuoFoldItem>()
isExpand 是否已展开 false
parentHash 父级hash,展开后才会赋值 false
sticky 该item是否吸顶 false
gridSpan 该item在grid中的占比 0
staggeredGridFullSpan 该item在staggeredGrid中是否占满 false

三、结语

首先感谢大家的阅读,马上就要过年了,在这里也祝大家新年快乐,希望这个库能够对你们有所帮助。如果你喜欢YasuoRecyclerViewAdapter这个库,希望能在github上给一个star,作为我进步的动力!
如果他有不足或需要新增的功能,可以向我提issue或添加我的qq:876625596

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

推荐阅读更多精彩内容