

Android入门第64天-MVVM下瀑布流界面的完美实现-使用RecyclerView
source link: https://blog.csdn.net/lifetragedy/article/details/129121343
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

网上充满着不完善的基于RecyclerView的瀑布流实现,要么根本是错的、要么就是只知其一不知其二、要么就是一充诉了一堆无用代码、要么用的是古老的MVC设计模式。
一个真正的、用户体验类似于淘宝、抖音的瀑布流怎么实现目前基本为无解。因为本人正好自己空闲时也在做前后台一体化开发,所以直接从本人自己项目里(mao-sir.com)公开一部分核心代码以便于加快大家的学习进度,以使得各位不要在这种小东西上折腾太多时间。
什么是瀑布流

注意看这边的这个布局,大家有没有发觉这些照片分成左右、最多的还有分成三列的,但是每一行的照片、视频、内容的高度是“错开”的。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数做的好的大厂的APP都是这种布局、尤以UGC(UGC它的中文意思是用户生产内容的意思,简称为UGC)为主的APP采用此布局最多像:知乎上的精品贴、推荐贴、小红书种草等都是这种风格。
瀑布流布局的优点为:
- 吸引用户,当用户在浏览瀑布流式布局的时候(这里抛开懒加载),用户会产生一种错觉,就是信息是不停的在更新的,这会激发用户的好奇心,使用户不停的往下滑动。
- 良好视觉体验,采用瀑布流布局的方式可以打破常规网站布局排版,给用户眼前一亮的新鲜感,用户在浏览内容时会感到很有新鲜感,带来良好的视觉体验。
- 更好的适应移动端,由于移动设备屏幕比电脑小,一个屏幕显示的内容不会非常多,因此可能要经常翻页。而在建网站时使用瀑布流布局,用户则只需要进行滚动就能够不断浏览内容。(这一点和懒加载有一点像)
怎么实现瀑布流
网上有一些第三方控件使用了瀑布流,但是这些第三方控件都已经废弃或者是停更了。这些第三方控件本人都用过,不是有各种BUG就是把问题搞了很复杂。这东西其实很简单,一天内就可以做出生产级别的应用了,哪有这么难。
难就是难在太多初学者为了赶项目或者说很多人急功近利,只想着copy paste,因此搞了一堆其实无用的代码还把问题“混搅”了。
因此本篇会从本质上还原最最干净的瀑布流,同时本人会基于mvvm设计模式来讲这个瀑布流。我们用的就是Android原生的控件:RecyclerView组件。下面是类淘宝、抖音用户极致体验的一个瀑布流“正例”(我们在文未还会给出一个体验不好的反例供读者对比)。

下面我们来看实现 。
基于MVVM设计模式的RecyclerView实现瀑布流代码
工程整体结构
这是一个使用androidx的基于mvvm的工程。
至于如何把一个工程变成androidx和mvvm此处就不再赘述了,在我前面的博客中已经写了很详细了。

activity_main.xml布局

瀑布流中具体的明细布局-rv_item.xml
在明细布局里,整体瀑布流墙就两个元素,一个是照片的url另一个是文本框,实现很简单。

现在就来看我们的代码。
RVBean.java

代码中它定义了两个元素,一个为文本框,一个为用于加载网络图片的url,网络图片我用的是我另一台VM上的Nginx做的静态图片资源服务。
RVAdapter.java

核心代码导读
- 这个adapter用的正是mvvm设计模式做的adapter;
- 这个adapter和网上那些错误、有坑的例子最大的不同在于getItemViewType方法内必须返回position,否则你的瀑布流在上划加载新数据时会产生界面内对照片重新进行左右切换、重排、或者把照片底部留出很大一块空白如:左边垂直排3张,右边一大边空白或者反之亦然的情况;
- 必须使用notifyItemRangeChanged来通知刷新新数据,网上很多例子用的是notifyDataSetChange或者是其它相关的notify,它们都是错的,这是因为RecyclerViewer在上划下划时会导致整个瀑布流重新布局、而RecyclerView里用的是Glide异步加载网络图片的,这会导致组件看到有一个组片就去开始计算它的高度而实际这个照片还未加载好因此才会导致RecyclerView在上划下划时整体布局重新刷新和重布局。一定记得这个notifyItemRangeChanged,同时这个方法在使用前加载所有的图片(list数据),传参有两个参数,参数1:加载新数据前原数据行.size(),参数2:新加载数据.size();
有了adapter我们来看我们的应用了。
应用前先别急,我们自定义了一个StaggeredGridLayoutManager。
自定义FullyStaggeredGridLayoutManager
这边先说一下,为什么要自定义这个StaggeredGridLayoutManager?
public class FullyStaggeredGridLayoutManager extends StaggeredGridLayoutManager {
大家可以认为,这个类是一个策略。这也是网上绝大部分教程根本不说的,这个策略就是从根本上避免RecyclerViewer在上划下划时不要进行左右切换、重新布局、图片闪烁以及“解决Scrollview中嵌套RecyclerView实现瀑布流时无法显示的问题,同时修复了子View显示时底部多出空白区域的问题”用的,我在代码中也做了注释。
此处要敲一下黑板了。因此整个RecyclerView要做到类淘宝、抖音的这种用户体验必须是adapter里的代码和这个自定义StaggeredGridLayoutManager结合起来才能做到。
因此我们下面就来看在MainActivity里如何把adapter结合着这个自定义的StaggeredGridLayoutManager的应用吧。
先上我们自定义的这个StaggeredGridLayoutManager-我们在此把它命名叫做:FullyStaggeredGridLayoutManager的全代码。
FullyStaggeredGridLayoutManager.java代码

MainActivity.java
从这儿开始我们要进入正题了,这边要说真正的RecyclerView的应用了。
在此演示代码块里为了同时便于初学者和正在寻找RecyclerView上划下划时错位过大、重新布局影响体验的老手同时阅读和查询问题时方便,我要说一下整个Demo代码运行的设计思路如下:
- 上手先加载12条数据;
- 对上划(手指按住屏幕向上拉),一直拉、拉、拉,拉到第12条,触发了RecyclerView的onScrollStateChanged中的“往上拉拉不动”事件后开始再加载6条数据,以模拟实际项目中的“翻页时走一下后台API取新数据”的过程;

核心代码导读:
以下这一陀就是我说的使用自定义的StaggeredGridLayoutManager+adapter中的事件覆盖一起实现了著名的RecyclerView上划下划时重新布局、翻页的梗。

同时,一定要记得覆盖RecyclerView的onScrolled事件,在事件中加入这样的代码
正确的做法
因此这边总结一下共有5个点,做到这5个点才能真正避免网上一堆的关于RecyclerView快速上划下划时整个界面产生了春左右列切换、重新布局、布局不合理如:左边垂直排了3-4个图片而把右边留出一大块空白的梗的综合手段:
- 必须使用一个自定义的StaggeredGridLayoutManager,你可以直接使用我博客中的代码,它来自于我正在制作的上生产的mao-sir.com的app中的代码,这是我的个人的一个开源中台产品;
- 必须要设置上面代码截图中的4个属性即:
SimpleItemAnimator里的setSupportsChangeAnimations(false);
DefaultItemAnimator里的setSupportsChangeAnimations(false);
setChangeDuration(0);
setHasFixedSize(true); -
必须要覆盖RecyclerView的onScrolled方法,在方法里设置防止第一行到顶部有空白的操作;
-
必须要在adapter里覆盖getItemViewType,在方法内返回position;
-
必须要在adapter里刷新数据时使用:notifyItemRangeChanged;
错误的做法
这边我说网上绝大部分示例是错的可能是客气了点,因该说可以搜到的全是错的。我们也来总结一下,希望各位不要再去踩这种坑了。
- 把图片的尺寸预先存在后台、每次接口取图片时后台把这个尺寸返回给到RecyclerView的adapter。。。这是得有多。。。无聊的做法,覆盖一个getItemViewType不就同样实现了这样的手法?
- setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)的做法是错的,设完后图片要么在一开始进入界面时显示不出要么显示不全,我不知道这个问题最早是谁想出的解决办法?怎么自己也不去验证一下对不对?
- 网上还有一种手法也是错的离谱,就是在onBindViewHolder方法里通过BitMap+GLIDE重设图片的尺寸,这种方法根本无效;
下面我们来感受一下使用了网上错误的手法后,这个瀑布流会变成什么样,大家就能感受到我说的这种:体验问题了。这种体验问题在APP上如果没有做好会直接要了你这家企业的“口碑的命”。
来看错误手法导致的差劲的瀑布流体验
请看下面的“反例”的演示,注意到了上划或者下划时左右列切换、重排的问题了没?给大家感受一下。

此处我们需要写一下RecyclerView在生产上真正使用时在设计上的一个补充点。
根据上面的代码我们可以看到我用了上划触发上划到一页底部并实现翻页的无缝衔接的体验。
但实际在生产上我们会遇到上千~近万张图片的前后台传输。如果图片一多、翻页一多,因为这个翻而是介于前后台交互的一个动作,因此有时在遇有弱网或者是网络抖动时卡死Android主进程并导致App闪退、卡屏、白屏等现象。
所以,我这边给出万级QPS并发下的瀑布流设计上的两个点:
- APP的首页、分类、搜索的list、瀑布流、三行三列这种界面的数据必须来自后台的es,而不是去什么后台查数据库这么来一下。在中/后台,我们通过企业内部的运营管理平台对于内容、商品进行“上下架”后会触发一个ES或者是RediSearch(我的开源产品里已经彻底不用ES了)的全量或者是增量索引;
- 在APP端,不能翻一页就去后台取一下!而是APP端也要做一层“缓存”,可以把已经获取到的数据存在本地的内嵌存储,本地取不到再去取后/中台的API接口获取数据;
以此充分极大化加快我们APP端的反映速度同时取得最优的用户体验。
好,结束今天的教程。大家不妨自己动一下手试试看效果去吧。
Recommend
-
95
2018年01月01日 阅读 4114 Android 从零开始实现RecyclerView分组及粘性头部效果 版权声明:本文为博主原创文...
-
50
背景 项目中要实现横向列表的无限循环滚动,自然而然想到了RecyclerView,但我们常用的RecyclerView是不支持无限循环滚动的,所以就需要一些办法让它能够无限循环。 方案选择 方案1 对Adapter进行修改 网上大部分博客的解决方案都是这种方案
-
52
前言 在Android中RecyclerView配合LayoutManager可以实现多种列表效果,比如可以实现横滑、纵滑列表的的LinearLayoutManager,网格布局的GridLayoutManager, 如果想实现瀑布流的效果,那么使用StaggeredGridLayoutManager即可,但StaggeredGridLay...
-
43
“潮流最前端,每周一新鲜而至” If not SPAs, What? 、 一周精读 《 设计模式 - SingleTon 单例模式》、Gazepas...
-
6
uni-app项目瀑布流布局,完美解决方案直接复制代码 <hd-list/> 列表布局数据自己写<template> <view class="waterfall-wrap"> <view class="waterfall...
-
3
瀑布流的实现思路:首先确定视口宽度 再确定第一行的每个块的宽度 通过两者来知道第一行有多少个块其次 将第一行的每个块的高度push到arr数组中存放 获取第一行块的距离左边的距离最后 将第二行的块放在第一行的高度最短的块下面 获取第...
-
31
variation8 2021-09-29 08:30 采纳率: 0% ECharts+Canvas 实现频谱瀑布图实现运行代码
-
5
卡片瀑布流实现如何实现下图瀑布流功能?瀑布流的核心就是找每一列中最小的高度,给当前列添加数据。图片中数据可以简化成如下格式const list = [ {title: '名...
-
4
Android入门第25天-Android里使用SimpleAdapter实现复杂的界面布局
-
7
Android 弧形 RecyclerView 实现(Kotlin) 2023年6月12日 | 最近更新于 下午5:11 项目改版需要实现一个圆弧效果的滑动列表,网上没找到很好的开源实现,自己改了一版,给出具体实现步骤和源码,以下...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK