分页加载?你还在用各种Refreshlayout?你Out了!哥哥教你Paging完美实现各种分页姿势

一,什么是Paging

Paging 可以使开发者更轻松在 RecyclerView 中 分页加载数据。

官方文档:https://developer.android.com/topic/libraries/architecture/paging/

Sample: https://github.com/googlesamples/android-sunflower

二,主要涉及问题

PageList

这个类是用来存储加载的数据。PagedList中所需要的数据都是从下面要讲的DataSource中进行加载的。

DataSource

这类提供加载所需的数据。也就是在这个类中进行数据的获取操作。数据源可以是DataBase也可以是服务器。

三,如何使用Paging

1,添加依赖

    //Paging的依赖

    implementation "android.arch.paging:runtime:1.0.0"

    //Paging对RxJava2的原生支持

    implementation "android.arch.paging:rxjava2:1.0.0-rc1"

2,创建数据源

PositionalDataSource: 主要用于加载数据可数有限的数据。比如加载本地数据库,这种情况下用户可以通过比如说像通讯录按姓的首字母查询的情况。能够跳转到任意的位置。                                                                                                            ItemKeyedDataSource:主要用于加载逐渐增加的数据。比如说网络请求的数据随着不断的请求得到的数据越来越多。然后它适用的情况就是通过N-1item的数据来获取Nitem数据的情况。                                                                                             PageKeyedDataSource:这个和ItemKeyedDataSource有些相似,都是针对那种不断增加的数据。这里网络请求得到数据是分页的。

DataSource.Factory

这个接口的实现类主要是用来获取的DataSource的。

PagedListAdapter

这个Adapter继承自RecyclerView.Adapter。如果要使用Paging,就需要让实现的RecyclerView的Adapter继承自PagedListAdapter。这个抽象类实现关于PagedList相关的东西。

LivePagedListBuilder

通过这个类来生成对应的PagedList 

创建工厂类用来创建DataSource对象

3,创建ViewModel对象

我们知道在jetpack中推荐每个页面持有一个VIewModel对象,用来保证数据的正确性以及避免其他问题。但是这里对ViewModel不做具体介绍。

4,Recyclerview

PagedListAdapter

在使用PagedListAdapter时,我们需要注意的是,PagedListAdapter内部默认实现DiffUtil来进行数据的差量计算,所以我们在构造方法里面传递一个DiffUtil.ItemCallback。

这里几种介绍一下是DiffUtil,用它来做什么?为什么会出现?

虽然 RecyclerView 提供的局部更新的方法,看似非常的好用,但是实际上,其实并没有什么用。在实际开发中,最方便的做法就是无脑调用 notifyDataSetChanged(),用于更新 Adapter 的数据集。虽然 notifyDataSetChanged() 有一些缺点:不会触发 RecyclerView 的局部更新的动画。性能低,会刷新整个 RecyclerView 可视区域。但是真有需要频繁刷新,前后两个数据集的场景。方案一:使用一个 notifyDataSetChanged() 方法。方案二:自己写一个数据集比对方法,然后去计算他们的差值,最后调用对应的方法更新到 RecyclerView 中去。我这么懒,如果不是必要,当然是会选 方案一 了。毕竟和之前 ListView 的时候,也没有更差了。Google 显然也发现了这个问题,所以 DiffUtil 被发布了 。

就像前面说的,DiffUtil 就是为了解决这个痛点的。它能很方便的对两个数据集之间进行比对,然后计算出变动情况,配合 RecyclerView.Adapter ,可以自动根据变动情况,调用 Adapter 的对应方法。

DiffUtil 在使用起来,主要需要关注几个类:                                                                DiffUtil.Callback:具体用于限定数据集比对规则。                                                      DiffUtil.DiffResult:比对数据集之后,返回的差异结果。

1,Diffutil.Callback

DiffUtil.Callback 主要就是为了限定两个数据集中,子项的比对规则。毕竟开发者面对的数据结构多种多样,既然没法做一套通用的内容比对方式,那么就将比对的规则,交还给开发者来实现即可。

在 Callback 中,其实只需要实现 4 个方法:

getOldListSize():旧数据集的长度。

getNewListSize():新数据集的长度

areItemsTheSame():判断是否是同一个Item。

areContentsTheSame():如果是通一个Item,此方法用于判断是否同一个 Item 的内容也相同。

后两个方法,主要是为了对应多布局的情况产生的,也就是存在多个 viewType 和多个 ViewHodler 的情况。首先需要使用 areItemsTheSame() 方法比对是否来自同一个 viewType(也就是同一个 ViewHolder ) ,然后再通过 areContentsTheSame() 方法比对其内容是否也相等。

其实 Callback 还有一个 getChangePayload() 的方法,它可以在 ViewType 相同,但是内容不相同的时候,用 payLoad 记录需要在这个 ViewHolder 中,具体需要更新的View。areItemsTheSame()、areContentsTheSame()、getChangePayload() 分别代表了不同量级的刷新。

首先会通过 areItemsTheSame() 判断当前 position 下,ViewType 是否一致,如果不一致就表明当前 position 下,从数据到 UI 结构上全部变化了,那么就不关心内容,直接更新就好了。如果一致的话,那么其实 View 是可以复用的,就还需要再通过 areContentsTheSame() 方法判断其内容是否一致,如果一致,则表示是同一条数据,不需要做额外的操作。但是一旦不一致,则还会调用 getChangePayload() 来标记到底是哪个地方的不一样,最终标记需要更新的地方,最终返回给 DiffResult 。当然,对性能要是要求没那么高的情况下,是可以不使用 getChangedPayload() 方法的。

2,DiffUtil.DiffResult

DiffUtil.DiffResult 其实就是 DiffUtil 通过 DiffUtil.Callback 计算出来,两个数据集的差异。它是可以直接使用在 RecyclerView 上的。如果有必要,也是可以通过实现 ListUpdateCallback 接口,来比对这些差异的。

3,使用DiffUtil

介绍了 Callback 和 DiffResult 之后,其实就可以正常使用 DiffUtil 来进行数据集的比对了。在这个过程中,其实真的很简单,只需要调用两个方法:

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(oldDatas, newDatas), true);diffResult.dispatchUpdatesTo(mAdapter);

calculateDiff 方法主要是用于通过一个具体的 DiffUtils.Callback 实现对象,来计算出两个数据集差异的结果,得到 DiffUtil.DiffResult  。而 calculateDiff 的另外一个参数,用于标记是否需要检测 Item 的移动。

DiffUtil 使用的是 Eugene Myers 的差别算法,这个算法本身是不检查元素的移动的。也就是说,有元素的移动它也只是会先标记为删除,然后再标记插入。而如果需要计算元素的移动,它实际上也是在通过 Eugene Myers 算法比对之后,再进行一次移动检查。所以,如果集合本身已经排序过了,可以不进行移动的检查。

而 dispatchUpdatesTo() 就是将这个数据集差异的结果,通过 Adapter 更新到 RecyclerView 上面。实际上 dispatchUpdatesTo(Adapter) ,也是使用的 ListUpdateCallback 这个接口,在其中获得差异,然后调用 Adapter 的对应方法。

Google 官方同时也指出,如果是对大数据集的比对,最好是放在子线程中去完成计算,也就是其实是存在堵塞 UI 的情况的。所以如果你遇见了使用 DiffUtil 之后,每次刷新有卡顿的情况,可以考虑是否数据集太大,是否应该在子线程中完成计算。

5,Activity中实战

实操图

Jetpack是Google官方推荐的一种App架构方案,在这种结构下,会避免很多的问题,比如说内存泄漏和空指针异常等等。之所以使用这种方案来架构App,不是我们吃饱撑的没事干,而是为了解决传统架构方案下不能解决的问题。当然可能还有其他的第三方解决方案,但是肯定不比Jetpack权威,毕竟Google爸爸亲自设计的。

横批:早日脱单

上联:愿诸君,添好运,代码无错,奖金成摞,

下联:祝各位,发大财,身强体壮,毛发挺旺。

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

推荐阅读更多精彩内容