(Java实现)使用官方Paging3分页库实现RecyclerView加载更多(loadmore)的功能
Paging 是 google 官方推出的一个分页加载库, 配合 RecyclerView 可以很方便实现 RecyelerView 的 Footer 和 Header,
Paging3 相对以前的版本, 实现 loadmore 的功能更为方便, 但因为找了一圈, 连 Google,stackoverflow,github 这些地方也找了一遍, 都没有找到关于 Paging3 的关于 java 实现代码
, 因此自己就根据一些案例, 参考了 Google 迁移到 Paging3 的说明, 实现了最基本的分页加载的功能. Footer 和 Header 的实现有时间可能会写一下.
因为网上也有了一些关于 Paging3 的教程以及说明. 所以这里就不再啰嗦, 具体就自己查阅相关资料.
2020.12.21 更新: 已上传项目至 github
项目地址: https://github.com/1831553190/paging3
正文:
使用 Paging3, 我们可以很方便的做出加载更多的功能, 只要滑动到特定的位置就可以自动加载下一页的内容.
Paging3 的功能实现, 可以使用使用 Rxjava 或者 LiveData, 本教程使用的是 LiveData 来实现;
现在的 Paging3 已经更新到了 alpha07 版, 因此我们引入 Paging 库
implementation "androidx.paging:paging-runtime:3.0.0-alpha07"
implementation "androidx.paging:paging-guava:3.0.0-alpha07" //LiveData
以及
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' //这里用到了ViewModelKt,所以需要引入,具体是什么玩意请自行搜索,这里不说明
PagingSource:(负责每一分页的数据加载)
Data 就是一个普通的实体类
class Data{
String title;
String coverImage;
public String getCoverImage(){
return coverImage;
}
public void setCoverImage(){
this.coverImage=coverImage;
}
public String getTitle(){
return title;
}
public void setTitle(){
this.title=title;
}
public String getCoverImage(){
return coverImage;
}
public void setCoverImage(){
this.coverImage=coverImage;
}
public String getTitle(){
return title;
}
public void setTitle(){
this.title=title;
}
}
public class LiveDataSource extends ListenableFuturePagingSource<Integer, Data> {
//需要用到线程池
private ListeningExecutorService executorService=MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
String query;
public LiveDataSource(String query){
this.query=query;//查询内容所需参数
}
@NotNull
@Override
public ListenableFuture<LoadResult<Integer, Data>> loadFuture(@NotNull LoadParams<Integer> params) {
Integer nextPageNumber = params.getKey();
if (nextPageNumber == null) {
nextPageNumber = 0;//从第0页开始加载
}
Integer finalNextPageNumber = nextPageNumber;
ListenableFuture<LoadResult<Integer, Data>> pageFuture =Futures
.transform(executorService.submit(new Callable<List<Data>>() {
@Override
public List<Data> call() throws Exception {
//这里处理耗时操作,比如网络请求数据,数据库数据加载
NetInterface netInterface = NetWorkUtils.getRetrofit().create(NetInterface.class);
Response<Msg<PageInfo>> infos = netInterface.getPageInfo(finalNextPageNumber,query).execute();
//这里用Roterfit请求网络数据,使用页数和构造函数传过来的参数作为请求参数来请求数据
return infos.body().getData().getList();//得到数据后返回
}
}), new Function<List<Data>, LoadResult.Page<Integer, Data>>() {
@NotNull
@Override
public LoadResult.Page<Integer, Data> apply(@Nullable List<Data> input) {
// 这里传入的三个参数中,刚才请求的数据,第二个参数为请求的上一页的页数,当为null时不再加载上一页,第三个参数则是下一页,后两个参数不介绍,自行了解
return new LoadResult.Page<>(input,finalNextPageNumber==0?null:finalNextPageNumber-1,input.isEmpty()?null:finalNextPageNumber+1);
}
}, executorService);
ListenableFuture<LoadResult<Integer,Data>> partialLoadResultFuture = Futures.catching(
pageFuture, Exception.class,
LoadResult.Error::new, executorService);
return Futures.catching(partialLoadResultFuture,
Exception.class, LoadResult.Error::new, executorService);
}
}
@NotNull
@Override
public ListenableFuture<LoadResult<Integer, Data>> loadFuture(@NotNull LoadParams<Integer> params) {
Integer nextPageNumber = params.getKey();
if (nextPageNumber == null) {
nextPageNumber = 0;//从第0页开始加载
}
Integer finalNextPageNumber = nextPageNumber;
ListenableFuture<LoadResult<Integer, Data>> pageFuture =Futures
.transform(executorService.submit(new Callable<List<Data>>() {
@Override
public List<Data> call() throws Exception {
//这里处理耗时操作,比如网络请求数据,数据库数据加载
NetInterface netInterface = NetWorkUtils.getRetrofit().create(NetInterface.class);
Response<Msg<PageInfo>> infos = netInterface.getPageInfo(finalNextPageNumber,query).execute();
//这里用Roterfit请求网络数据,使用页数和构造函数传过来的参数作为请求参数来请求数据
return infos.body().getData().getList();//得到数据后返回
}
}), new Function<List<Data>, LoadResult.Page<Integer, Data>>() {
@NotNull
@Override
public LoadResult.Page<Integer, Data> apply(@Nullable List<Data> input) {
ListenableFuture<LoadResult<Integer,Data>> partialLoadResultFuture = Futures.catching(
pageFuture, Exception.class,
LoadResult.Error::new, executorService);
return Futures.catching(partialLoadResultFuture,
Exception.class, LoadResult.Error::new, executorService);
}
ViewModel:(数据更新观察)
public class LoadMoreViewModel extends ViewModel {
MutableLiveData<PagingData<Data>> dataMutableLiveData=new MutableLiveData<>();
PagingConfig pagingConfig=new PagingConfig(10,10,false,10);//初始化配置,可以定义最大加载的数据量
public LiveData<PagingData<Data>> getPaging(){
CoroutineScope viewmodelScope= ViewModelKt.getViewModelScope(this);
Pager<Integer, Data> pager = new Pager<Integer, Data>(pagingConfig, ()->new LiveDataSource("queryId"));构造函数根据自己的需要来调整
return PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager),viewmodelScope);
}
public LiveData<PagingData<Data>> getPaging(){
CoroutineScope viewmodelScope= ViewModelKt.getViewModelScope(this);
Pager<Integer, Data> pager = new Pager<Integer, Data>(pagingConfig, ()->new LiveDataSource("queryId"));构造函数根据自己的需要来调整
return PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager),viewmodelScope);
}
}
UI 则观察数据的更新, 并加载数据
UI:(Fragment 或者 Activity), 可以在 onCreate() 或者 onCreateView() 里面写
listAdapter =new ListAdapter(new DiffUtil.ItemCallback<Data>() {
@Override
public boolean areItemsTheSame(@NonNull Data oldItem, @NonNull Data newItem) {
return oldItem.getId()==newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull Data oldItem, @NonNull Data newItem) {
return oldItem.getTitle().equals(newItem.getTitle());
}
});
recyclerView.setAdapter(listAdapter);
LoadMoreViewModel loadMoreViewModel=new ViewModelProvider(this).get(LoadMoreViewModel.class);
loadMoreViewModel.getPaging().observe(getViewLifecycleOwner(),
dataInfoPagingData -> listAdapter.submitData(getLifecycle(),dataInfoPagingData));//观察数据的更新</code></pre>
Adapter:(基本和原来一样,但是继承的类不一样了)
public class ListAdapter extends PagingDataAdapter<Data, ListAdapter.Holder> {
public ListAdapter(@NotNull DiffUtil.ItemCallback<Data> diffCallback) {
super(diffCallback);
}
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_info,parent,false));
}
@Override
public void onBindViewHolder(@NonNull Holder holder, int position) {
holder.title.setText(getItem(position).getTitle());
Glide.with(holder.itemView.getContext())
.load(getItem(position).getCoverImage())
.crossFade()
.error(R.mipmap.img)
.into(holder.cover);
}
class Holder extends RecyclerView.ViewHolder{
TextView title;
ImageView cover;
public Holder(@NonNull View itemView) {
super(itemView);
title=itemView.findViewById(R.id.text_title);
cover=itemView.findViewById(R.id.img_coverPicture);
}
}
}
@Override
public boolean areContentsTheSame(@NonNull Data oldItem, @NonNull Data newItem) {
return oldItem.getTitle().equals(newItem.getTitle());
}
});
recyclerView.setAdapter(listAdapter);
LoadMoreViewModel loadMoreViewModel=new ViewModelProvider(this).get(LoadMoreViewModel.class);
loadMoreViewModel.getPaging().observe(getViewLifecycleOwner(),
dataInfoPagingData -> listAdapter.submitData(getLifecycle(),dataInfoPagingData));//观察数据的更新</code></pre>
public class ListAdapter extends PagingDataAdapter<Data, ListAdapter.Holder> {
public ListAdapter(@NotNull DiffUtil.ItemCallback<Data> diffCallback) {
super(diffCallback);
}
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_info,parent,false));
}
@Override
public void onBindViewHolder(@NonNull Holder holder, int position) {
holder.title.setText(getItem(position).getTitle());
Glide.with(holder.itemView.getContext())
.load(getItem(position).getCoverImage())
.crossFade()
.error(R.mipmap.img)
.into(holder.cover);
}
class Holder extends RecyclerView.ViewHolder{
TextView title;
ImageView cover;
public Holder(@NonNull View itemView) {
super(itemView);
title=itemView.findViewById(R.id.text_title);
cover=itemView.findViewById(R.id.img_coverPicture);
}
}
public ListAdapter(@NotNull DiffUtil.ItemCallback<Data> diffCallback) {
super(diffCallback);
}
@NonNull
@Override
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_info,parent,false));
}
@Override
public void onBindViewHolder(@NonNull Holder holder, int position) {
holder.title.setText(getItem(position).getTitle());
Glide.with(holder.itemView.getContext())
.load(getItem(position).getCoverImage())
.crossFade()
.error(R.mipmap.img)
.into(holder.cover);
}
class Holder extends RecyclerView.ViewHolder{
TextView title;
ImageView cover;
public Holder(@NonNull View itemView) {
super(itemView);
title=itemView.findViewById(R.id.text_title);
cover=itemView.findViewById(R.id.img_coverPicture);
}
}
}