1.前言
在RecyclerView上使用StaggeredGridLayoutManager布局管理器很容易实现瀑布流布局。瀑布流布局比线性布局和网格布局美观,手机屏幕空间利用率高,但是实现方式也比它们复杂,而且经常会有一些莫名奇怪的bug会在瀑布流布局上出现,线性和网格则不会。
本来将重点介绍瀑布流两个臭名昭著的bug。通过深入探索瀑布流布局的实现原理,分析它们的形成原因,并给出优雅解决它们的解决方案。
1.1 bug1:顶部空白、重排序
bug复现操作:
- 将瀑布流滑动到底部;
- 点击刷新按钮,调用Adapter.notifyDataSetChanged();
- 从下往上滑动瀑布流列表至顶部时手指离开屏幕。
bug描述
当滑动到顶部时,顶部出现空白,松手时瀑布流发生重排序,并且触发了动画。
在实际的项目中,我们不太希望用户看到这种明显的动画,甚至不想用户看到瀑布流顶部出现空白。
1.2 bug2:瀑布流左右间距错乱
bug复现操作同bug1
bug描述
“1汉皇重色…”和“5云鬓花颜…”左侧没有对齐。整个瀑布流布局有很多这样的间距错乱问题。
2. 解决方案
2.1 bug1解决方案
网上的解决方案如下:
该方法能够解决问题,但是比较耗性能。因为,它不管会不会出现空白情况,都会清空mLazySpanLookup,并且重新布局。
我的解决方案是,通过反射StaggeredGridLayoutManager的checkForGaps()。在滑动时,仅在有需要的情况下才会重新布局。
效果如下:同样的操作,当滑到顶部时,空白问题解决了。但是bug2的问题仍然存在
2.2 bug2解决方案
本案例中,瀑布流每个Item之间的间隔是10dp。屏幕两侧与Item之间的间隔也是10dp。我是通过ItemDecoration来实现的。
解决方案如下:
完美解决间距错乱问题。
3. bug2产生原因分析
瀑布流Span数组
在本案例中,瀑布流共四列。屏幕被分割成四个Span。每个Span对应的下标分别是0、1、2、3。而左右间距正是根据下标变化的。如果下标不正确,那么ItemView的间距也会计算错误。比如说“1汉皇重色…”的span本来应该是0,如果变成了1,那么它的左边距和右边距就是5dp。而正确的应该是左间距10dp,右间距5dp。
观察spanIndex错乱
我分别在getItemOffsets()和animateMove()方法中打印spanIndex
zijiexiaozhan getItemOffsets 1汉皇重色思倾国,御宇多年求不得 index 1
zijiexiaozhan animate 1汉皇重色思倾国,御宇多年求不得 index 0
我们知道“1汉皇重色思倾国”的正确的spanIndex是0。而在getItemOffsets中却是1,在做动画的时候又变成了正确的0。原因可能有两种
- getItemOffsets在设置spanIndex之前调用,导致getItemOffsets拿不到正确的spanIndex
- getItemOffsets在设置spanIndex之后调用,但是多次设置spanIndex,由于某种原因getItemOffsets并没有同步调用
分析fill方法
由图我们可以看到设置spanIndex会在getItemOffsets方法前面调用,所以排除第一种可能性
分析getItemOffsets调用时机
在getItemOffsets中打断点,获得调用栈如下
//RecyclerView.java
原因是lp.mInsetsDirty在dispatchLayoutStep1阶段被设置成true了。导致在dispatchLayoutStep2真正布局阶段,不会调用到getItemOffsets方法。从而导致缺失了一次更正Decoration绘制的机会。关于RecyclerView动画原理,请查阅RecyclerView布局和动画原理一文。
解决方案
解决方法就显而易见了,在checkForGaps返回true后把lp.mInsetsDirty设成false。反射调用RecyclerView的markItemDecorInsetsDirty()
4.总结
RecyclerView是Android UI框架中一个非常重要的组件。它使用简单,上手快。但是它的高级使用,一旦遇到问题,那就会变成一件非常棘手的事情。因为我专门写了一系列关于RecyclerView高级进阶的文章。包含了布局原理、动画原理、滑动原理、缓存实现机制、实战踩坑填坑等方面。相信你学习完一定会有收货。
来源:https://juejin.cn/post/6978113728202047502
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20205,转载请注明出处。
评论0