本文主要是介绍写一个万用RecyclerView分隔线,支持linear grid staggered,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言
2023已过半,才发现我已经大半年没写博客了,痛定思痛决定水一篇。
不知道大家平时干活的时候有没有被RecyclerView列表的分隔线困扰过,app里一般都会有各种各样的列表,横的竖的、网格、瀑布流样式的,每次要给列表设分隔线时都要自己写一个特化的ItemDecoration,既麻烦又难以复用,那能不能写一个适用大多数场景的ItemDecoration来减轻这类负担呢?
别急,本篇文章就给大家带来一个我自用的通用ItemDecoration,支持linear grid staggered LayoutManager,支持横竖向、跨列等情况;支持边缘、横纵向分隔线不同宽度,使用也非常简单。
效果图和代码
代码
单个类可以直接使用,仓库包含demo
效果图 网格样式
瀑布流
这个ItemDecoration暂时没有实现分隔线上色,因为我觉得这种场景其实很少就把相关代码删掉了,要加的话建议通过继承实现。
实现和注意点
首先,由于要支持横竖向,所以定义两个轴,主轴代表可滑动的那个轴,交叉轴代表另一个轴,这样无论是横向还是竖向都能保持语义一致
// 主轴方向分割线宽度
protected var mainWidth = 0// 交叉轴方向分割线宽度
protected var crossWidth = 0// 边缘宽度
protected var mainPadding = 0
protected var crossPadding = 0
主轴的间隔
主轴的分隔线很简单,第一行的item和最后一行的item设置边缘间隔,其他每个item在主轴同一方向上设置分隔线间隔,关键点在于首行和末行的判断。
LinearLayoutManager情况下最简单,判断position是首个或者最后一个就ok了,但是GridLayoutManager和StaggeredGridLayoutManager都存在跨列问题。比如说列表有5列,但是第一个item就占满了整行,那么本该在第一行的2-5个item实际上就不在第一行了;末行判断同理。
GridLayoutManager通过它的SpanSizeLookup来判断,groupIndex0在首行,groupIndexlastGroupIndex在最后一行
// 当前item在哪一行
val groupIndex = manager.spanSizeLookup.getSpanGroupIndex(position, spanCount)
// 最后一个item在哪一行
val lastGroupIndex = manager.spanSizeLookup.getSpanGroupIndex(size - 1, spanCount)
StaggeredGridLayoutManager相对麻烦一些,看下面的注释,spanIndex代表当前item在本行内的下标
val lp = view.layoutParams
if (lp is StaggeredGridLayoutManager.LayoutParams) {val spanCount = manager.spanCount// 前面没有跨列item时当前item的期望下标val exceptSpanIndex = position % spanCount// 真实的item下标val spanIndex = lp.spanIndex// position原属于第一行并且此item之前没有跨列的情况,当前item才属于第一行val isFirstGroup = position < spanCount && exceptSpanIndex == spanIndexvar isLastGroup = falseif (size - position <= spanCount) {// position原属于最后一行val lastItemView = manager.findViewByPosition(size - 1)if (lastItemView != null) {val lastLp = lastItemView.layoutParamsif (lastLp is StaggeredGridLayoutManager.LayoutParams) {// 列表最后一个item和当前item的spanIndex差等于position之差说明它们之间没有跨列的情况,当前item属于最后一行if (lastLp.spanIndex - spanIndex == size - 1 - position) {isLastGroup = true}}}}
}
接下来就很简单了,设置主轴上的间隔
if (isFirstGroup) {// 是第一行if (isVertical) {outRect.top = mainPadding} else {outRect.left = mainPadding}
} else if (isLastGroup) {// 是最后一行要加边缘if (isVertical) {outRect.top = mainWidthoutRect.bottom = mainPadding} else {outRect.left = mainWidthoutRect.right = mainPadding}
} else {if (isVertical) {outRect.top = mainWidth} else {outRect.left = mainWidth}
}
交叉轴的间隔
交叉轴的分隔线最简单的是LinearLayoutManager,由于不存在多列直接设置为边缘间隔就可以了
if (isVertical) {outRect.left = crossPaddingoutRect.right = crossPadding
} else {outRect.top = crossPaddingoutRect.bottom = crossPadding
}
GridLayoutManager和StaggeredGridLayoutManager的交叉轴分隔线计算方法是一样的,可以统一处理,需要遵循的规则有两个
- 每列占用的左右间隔之和相等
- 每个item占用的右间隔和它相邻item占用的左间隔之和等于给定的间隔宽度
以下图为例,列表共4列,边缘间隔是15,item间隔是10,第二个item跨两列,每列应该占用的空间为15。
以第3个item为例,如何计算出它的左间隔(第三个绿色部分的宽度)和右间隔(第二个红色部分的宽度,公式如下
左间隔:到当前item的左边为止的总间隔(crossWidth * spanIndex + crossPadding)减去 到上一个item为止需要使用的总间隔(spanUsedWidth * spanIndex),这个例子中这两个值相等
同理右间隔:到当前item为止需要使用的总间隔(spanUsedWidth * (spanIndex + spanSize)) 减去 到当前item右边为止的总间隔(crossWidth * (spanIndex + spanSize - 1) + crossPadding);当然也可以用 当前item需要使用的总间隔( spanUsedWidth * spanSize) - 当前item已经使用的总间隔( crossWidth * (spanSize - 1) + lt)
这样通过归纳只使用两行代码就统合了所有情况
/*** 交叉轴间隔* [spanIndex] 当前item的以第几列开始* [spanSize] 当前item占用的列数*/
private fun getItemCrossOffsets(outRect: Rect, isVertical: Boolean, spanCount: Int, spanIndex: Int, spanSize: Int) {// 每列占用的间隔val spanUsedWidth = (crossPadding * 2 + crossWidth * (spanCount - 1)) / spanCount// 到当前item的左边为止的总间隔 - 到上一个item为止需要使用的总间隔val lt = crossWidth * spanIndex + crossPadding - spanUsedWidth * spanIndex// 到当前item为止需要使用的总间隔 - 到当前item右边为止的总间隔
// val rb = spanUsedWidth * (spanIndex + spanSize) - crossWidth * (spanIndex + spanSize - 1) - crossPadding// 当前item需要使用的总间隔 - 当前item已经使用的总间隔val rb = spanUsedWidth * spanSize - crossWidth * (spanSize - 1) - ltif (isVertical) {outRect.left = ltoutRect.right = rb} else {outRect.top = ltoutRect.bottom = rb}
}
作者:北野青阳
原文链接:https://juejin.cn/post/7248811984749527101
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
这篇关于写一个万用RecyclerView分隔线,支持linear grid staggered的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!