Jetpack-生命周期组件库Paging

Paging#

用途#

对数据按需加载,实现无限滚动,支持从本地存储和网络加载。

优势#

  • 支持与Jetpack包下的库组合使用
  • 支持第三方库 RxJava

基本用法#

增加依赖#

1
2
3
4
5
6
// paging
implementation "androidx.paging:paging-runtime:2.1.0-alpha01"

implementation 'com.squareup.retrofit2:retrofit:2.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'

常见API#

参考https://segmentfault.com/a/1190000038953354

使用Paging库进行分页加载时,需要用到几个核心的类,分别是PagedListAdapter、PageList和DataSource。

API 用途
PagedListAdapter RecyclerView的适配器,扩展了原生的差分更新功能
PagedList 数据结构集合,存储DataSource中分页不变的内容列表,按条件获取数据
DataSource 加载数据至PagedList
DataSource.Factory 创建DataSrouce
LivePagedListBuilder 设置分页加载的配置项,设置DataSource.Factory,设置DataSource,返回LiveData
LoadParams 加载的关键信息,包括加载的键,和加载的页数
LoadResult 加载成功返回LoadResult.Page,加载失败返回LoadResult.Error
PagedListAdapter#

众所周知,在Android列表开发中需要使用RecyclerView,并且需要配合自定义AdapterPagedListAdapter继承于RecyclerView.Adapter,这表明它也是一个RecyclerView.Adapter,并且扩展了RecyclerView.Adapter的支持异步差分更新功能,该功能依赖于匿名内部类 DiffUtil.ItemCallback<T>,使用时必须复写areItemsTheSameareContentsTheSame方法。

PageList#

PageList是用于通知DataSource何时获取数据,以及如何获取数据。比如,何时获取第一页数据,以及何时开始加载数据等待。并且,DataSource数据源都将通过PageList设置给PagedListAdapter

LivePagedListBuilder

设置分页加载的配置项,通过传入DataSource.Factory,在Factorycreate方法里,返回DataSource,返回Adapter可以使用的LiveData,并通知页面,将新数据刷新到布局上;通过传入PagedList.Config设置分页参数如pageSizeprefetchDistance

DataSource#

DataSource主要用于执行数据的加载操作,数据载入操作必须在子线程中进行,否则会造成主线程的阻塞。DataSource的来源可以是网路,也可以是本地的数据库,如Room。根据分页机制的不同,DataSource可以有3种来源,分别是PageKeyedDataSource、PositionalDataSource和ItemKeyedDataSource。

  • PageKeyedDataSource:根据传入的页面num获取某一页的数据,比如获取第2页的数据。
  • PositionalDataSource:分页时默认显示的第几页。
  • ItemKeyedDataSource:封装了上一页、下一页的能力

Demo设计#

1657811159650

好文章推荐阅读https://juejin.cn/post/6844903976777809928#heading-24

Paging加载更多的机制#

如何找刷新的位置?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PagedList#BoundaryCallback
BoundaryCallback#onItemAtEndLoaded
PagedList#dispatchBoundaryCallbacks
PagedList#tryDispatchBoundaryCallbacks
PagedList#loadAround
PagedList#loadAround
AsyncPagedListDiffer#latchPagedList

AsyncPagedListDiffer#getItem

PagedList#deferBoundaryCallbacks



PagedList刷新位置1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Nullable
public T getItem(int index) {
if (mPagedList == null) {
if (mSnapshot == null) {
throw new IndexOutOfBoundsException(
"Item count is zero, getItem() call is invalid");
} else {
return mSnapshot.get(index);
}
}

mPagedList.loadAround(index);
return mPagedList.get(index);
}

PagedList刷新位置2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@SuppressWarnings("WeakerAccess") /* synthetic access */
void latchPagedList(
@NonNull PagedList<T> newList,
@NonNull PagedList<T> diffSnapshot,
@NonNull DiffUtil.DiffResult diffResult,
int lastAccessIndex,
@Nullable Runnable commitCallback) {
if (mSnapshot == null || mPagedList != null) {
throw new IllegalStateException("must be in snapshot state to apply diff");
}

PagedList<T> previousSnapshot = mSnapshot;
mPagedList = newList;
mSnapshot = null;

// dispatch update callback after updating mPagedList/mSnapshot
PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
previousSnapshot.mStorage, newList.mStorage, diffResult);

newList.addWeakCallback(diffSnapshot, mPagedListCallback);

if (!mPagedList.isEmpty()) {
// Transform the last loadAround() index from the old list to the new list by passing it
// through the DiffResult. This ensures the lastKey of a positional PagedList is carried
// to new list even if no in-viewport item changes (AsyncPagedListDiffer#get not called)
// Note: we don't take into account loads between new list snapshot and new list, but
// this is only a problem in rare cases when placeholders are disabled, and a load
// starts (for some reason) and finishes before diff completes.
int newPosition = PagedStorageDiffHelper.transformAnchorIndex(
diffResult, previousSnapshot.mStorage, diffSnapshot.mStorage, lastAccessIndex);

// Trigger load in new list at this position, clamped to list bounds.
// This is a load, not just an update of last load position, since the new list may be
// incomplete. If new list is subset of old list, but doesn't fill the viewport, this
// will likely trigger a load of new data.
mPagedList.loadAround(Math.max(0, Math.min(mPagedList.size() - 1, newPosition)));
}

onCurrentListChanged(previousSnapshot, mPagedList, commitCallback);
}

点击查看
-------------------本文结束 感谢您的阅读-------------------