本文根据以下思路进行Paging的学习:
许多应用程序使用包含大量项目的数据源中的数据,但一次只显示一小部分。
分页库可帮助您的应用观察并显示此数据的合理子集。此功能有几个优点:
来源自官方翻译
简单点说,
Paging给开发者提供了更为便捷的RecycleView数据加载方式。
下图表示Paging工作中数据的流向。
注:想一下水泵抽水过程。螺旋桨吸水、滤网过滤、组建水泵。
同时,Paging把大部分的工作都放在了子线程做,UI线程只负责数据的显示。
获取数据的类,Paging提供了三种不同的分页方式:PageKeyedDataSource、PositionalDataSource、ItemKeyedDataSource。
通常使用在论坛,贴吧中。根据传入的页面num进行获取某一页的数据,比如获取第10页。
Android开发中经常使用的分页,例如,每页10条,默认显示第一页。 CONTENT_LENGTH=10、mPage=1。
使用上一条data对象里某一个值作为下一页请求的关键字。这种情况多出现在服务端已经完成了分页效果,在某一个对象添加如“下一页”这样的标识数据。
上述组件根据业务逻辑不通使用不同类型。常用的为PositionalDataSource
使用以上之一的数据加载源需要跟下一个组件进行组合配置使用。
PageList提供了一系列的配置接口,方便开发者便捷的应付变幻莫测的需求。
/**
* When {@link #maxSize} is set to {@code MAX_SIZE_UNBOUNDED}, the maximum number of items
* loaded is unbounded, and pages will never be dropped.
*/
// MAX_SIZE=MAX_SIZE_UNBOUNDED的情况,数据会一直加载
@SuppressWarnings("WeakerAccess")
public static final int MAX_SIZE_UNBOUNDED = Integer.MAX_VALUE;
/**
* Size of each page loaded by the PagedList.
*/
// 每个界面加载数据的长度 CONTENT_LENGTH
public final int pageSize;
/**
* Prefetch distance which defines how far ahead to load.
*
* If this value is set to 50, the paged list will attempt to load 50 items in advance of
* data that's already been accessed.
*
* @see PagedList#loadAround(int)
*/
// 预加载 数据长度。 设置为50,当你看到第一个数据时候,第50条数据已加载完成
@SuppressWarnings("WeakerAccess")
public final int prefetchDistance;
/**
* Defines whether the PagedList may display null placeholders, if the DataSource provides
* them.
*/
// 数据没有加载成功时候,底部的占位符,相当于"加载更过"
@SuppressWarnings("WeakerAccess")
public final boolean enablePlaceholders;
/**
* Defines the maximum number of items that may be loaded into this pagedList before pages
* should be dropped.
*
* {@link PageKeyedDataSource} does not currently support dropping pages - when
* loading from a {@code PageKeyedDataSource}, this value is ignored.
*
* @see #MAX_SIZE_UNBOUNDED
* @see Builder#setMaxSize(int)
*/
// 最大加载量,数据加载到最大量之后,将会删除
public final int maxSize;
/**
* Size hint for initial load of PagedList, often larger than a regular page.
*/
// 一个提示大小
@SuppressWarnings("WeakerAccess")
public final int initialLoadSizeHint;
上述的配置已经满足我们基本需求,根据不同的场景设置不同的pageSize,达到更好的用户体验。
上面我们已经讲过了两个组件,分别是DataSource(螺旋桨)、PagedList(过滤网)。
接下来我们就要把这些组装起来,完成build过程。
该组件提供构造函数满足我们组件的需求,
mLiveData = new LivePagedListBuilder(new PageDataSourceFactory(positionalDataSource)
, new PagedList.Config.Builder().setPageSize(CONTENT_LENGTH)
.setPrefetchDistance(CONTENT_LENGTH)
.setEnablePlaceholders(false).setInitialLoadSizeHint(CONTENT_LENGTH)
.build()).build();
可以看到,通过该类的构造函数,将DataSource与PagedList绑定在一起,完成build.
那这个Builder返回的对象是一个什么样的类型呢???
LiveData> mLiveData
分页库实现了应用程序体系结构指南中建议的观察者模式 。特别是,库的核心组件创建LiveDataUI可以观察的实例 (或等效的基于RxJava2的类)。然后,您的应用程序的UI可以PagedList在生成对象时显示对象中的内容 ,同时尊重UI控制器的 生命周期。
返回的是一个LiveData类型的对象。他负责把获取的数据发送给到主线程。(水泵口接上了水管)。
注:还有一个需要注意的是RecycleView的adapter需要继承Paging自定义的PageAdapter。
public class MyAdapter extends PagedListAdapter {
private Context mContext;
protected MyAdapter(@NonNull DiffUtil.ItemCallback diffCallback) {
super(diffCallback);
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
return new BaseViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_main, parent, false));
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
TextView tvName = holder.getView(R.id.tv_name);
ImageView imageView = holder.getView(R.id.iv);
GankData data = getItem(position);
tvName.setText(position+"");
GlideUtils.loadImage(mContext,imageView,data.getUrl());
}
}
优点:Paging使得分页变得逻辑清晰、高效、便捷、优雅。同时又提升了用户体验。被分页问题搞到的同学可以尝试使用Paging作为解决方案。
缺点:配置相对繁琐,不同的界面界面的代码相对增加,初次使用比较麻烦(再加上AndroidX的刺激问题)
好了,Paging的初识至此告一段落,下一步我们来上手使用,感受以下它的魅力。