RecyclerView高级进阶之优雅地解决瀑布流的两个神坑

简介: RecyclerView高级进阶之优雅地解决瀑布流的两个神坑

1.前言


在RecyclerView上使用StaggeredGridLayoutManager布局管理器很容易实现瀑布流布局。瀑布流布局比线性布局和网格布局美观,手机屏幕空间利用率高,但是实现方式也比它们复杂,而且经常会有一些莫名奇怪的bug会在瀑布流布局上出现,线性和网格则不会。

本文将重点介绍瀑布流两个臭名昭著的bug。通过深入探索瀑布流布局的实现原理,分析它们的形成原因,并给出优雅的解决方案。


1.1 bug1:顶部空白、重排序

image.png

「bug复现操作:」


  1. 将瀑布流滑动到底部;
  2. 点击刷新按钮,调用Adapter.notifyDataSetChanged();
  3. 从下往上滑动瀑布流列表至顶部时手指离开屏幕。


「bug描述」


当滑动到顶部时,顶部出现空白,松手时瀑布流发生重排序,并且触发了动画。

在实际的项目中,我们不太希望用户看到这种明显的动画,更不想用户看到瀑布流顶部出现空白。


1.2 bug2:瀑布流左右间距错乱

image.png


「bug复现操作同bug1」


「bug描述」


“1汉皇重色...”和“5云鬓花颜...”左侧没有对齐。整个瀑布流布局有很多这样的间距错乱问题。

2. 解决方案


2.1 bug1解决方案


网上的解决方案如下:

640.png


该方法能够解决问题,但是比较耗性能。因为,它不管会不会出现空白情况,都会清空mLazySpanLookup,并且重新布局。


我的解决方案是,通过反射StaggeredGridLayoutManager的checkForGaps()。在滑动时,仅在有需要的情况下才会重新布局。

640.png

效果如下:同样的操作,当滑到顶部时,空白问题解决了。但是bug2的问题仍然存在


image.png


2.2 bug2解决方案


本案例中,瀑布流每个Item之间的间隔是10dp。屏幕两侧与Item之间的间隔也是10dp。我是通过ItemDecoration来实现的。

640.png

解决方案如下:

640.png


完美解决间距错乱问题。


3. bug2产生原因分析


瀑布流Span数组


在本案例中,瀑布流共四列。屏幕被分割成四个Span数组。数组中每个Span对应的下标分别是0、1、2、3。而左右间距正是根据下标变化的。如果下标不正确,那么ItemView的间距也会计算错误。比如说“1汉皇重色...”的span本来应该是0,如果变成了1,那么它的左边距和右边距就是5dp。而正确的应该是左间距10dp,右间距5dp。

image.png

观察spanIndex错乱


我分别在getItemOffsets()和animateMove()方法中打印spanIndex

640.png

zijiexiaozhan getItemOffsets 1汉皇重色思倾国,御宇多年求不得 index 1

zijiexiaozhan animate 1汉皇重色思倾国,御宇多年求不得 index 0

我们知道“1汉皇重色思倾国”正确的spanIndex是0。而在getItemOffsets中却是1,在做动画的时候又变成了正确的0。原因可能有两种


  1. getItemOffsets在设置spanIndex之前调用,导致getItemOffsets拿不到正确的spanIndex


  1. getItemOffsets在设置spanIndex之后调用,但是多次设置spanIndex,由于某种原因getItemOffsets并没有同步调用


分析fill方法

640.png

由图我们可以看到设置spanIndex会在getItemOffsets方法前面调用,所以排除第一种可能性


分析getItemOffsets调用时机


在getItemOffsets中打断点,获得调用栈如下

640.jpg

//RecyclerView.java

640.png

原因是lp.mInsetsDirty在dispatchLayoutStep1阶段被设置成true了。导致在dispatchLayoutStep2真正布局阶段,不会调用到getItemOffsets方法。从而导致缺失了一次更正Decoration绘制的机会。关于RecyclerView动画原理,请查阅RecyclerView布局和动画原理一文


解决方案


解决方法就显而易见了,在checkForGaps返回true后把lp.mInsetsDirty设成false。反射调用RecyclerView的markItemDecorInsetsDirty()

640.png

4.总结


RecyclerView是Android UI框架中一个非常重要的组件。它使用简单,上手快。但是它的高级使用,一旦遇到问题,那就会变成一件非常棘手的事情。因此我专门写了一系列关于RecyclerView高级进阶的文章。包含了「布局原理、动画原理、滑动原理、缓存实现机制、实战踩坑填坑」等方面。相信你学习完一定会有收货。

相关文章
|
7月前
|
Android开发 Kotlin
android开发,使用kotlin学习滚动控件RecyclerView
android开发,使用kotlin学习滚动控件RecyclerView
186 0
自己动手写RecyclerView的上拉加载
自己动手写RecyclerView的上拉加载
实用技巧 | RecyclerView 设置最大高度
实用技巧 | RecyclerView 设置最大高度
html+css实战184-布局完成
html+css实战184-布局完成
129 0
html+css实战184-布局完成
老大爷都能看懂的RecyclerView动画原理
老大爷都能看懂的RecyclerView动画原理
老大爷都能看懂的RecyclerView动画原理
【Flutter】ListView 列表高级功能 ( ScrollController 上拉加载更多 )
【Flutter】ListView 列表高级功能 ( ScrollController 上拉加载更多 )
628 0
【Flutter】ListView 列表高级功能 ( ScrollController 上拉加载更多 )
|
容器
Flutter 72: 图解极简自定义跑马灯 ACEMarquee
0 基础学习 Flutter,第七十二步:自定义极简模式跑马灯组件!
1765 0
|
缓存 Java Android开发
RecyclerView瀑布流优化方案探讨
目录介绍 01.规则瀑布流实现 02.不规则瀑布流实现 2.1 实现方式 2.2 遇到问题 03.瀑布流上拉加载 04.给瀑布流设置分割线 05.自定义Manager崩溃 06.如何避免刷新抖动 07.为何有时出现跳动 08.瀑布流图片优化 09.onBindViewHolder优化 10.瀑布流item点击事件优化 11.Glide加载优化 12.建议指定图片的宽高 欢迎同行探讨瀑布流极致优化方案 如果同行看到这篇文章,有好的瀑布流优化方案,欢迎给出建议,或者给链接也可以。
1965 0
|
Android开发
Flutter 18: 图解 ListView 下拉刷新与上拉加载 (二)【NotificationListener】
0 基础学习 Flutter,第十八步:ListView 上拉加载更多与下拉刷新,解决方案二!
5109 0
|
缓存 Android开发
RecyclerView实现探探卡片滑动功能及优化
title: RecyclerView实现探探卡片滑动功能 date: 2018-10-07 10:35:56 tags: RecyclerView 代码实现 博客地址:https://blog.csdn.net/qq_39085422/article/details/78612132 我只掌握了RecyclerView基础用法,所以参考别人博客的代码实现。
2145 0