android+上拉更多框架,Android Jetpack架构组件-Paging自定义上拉加载更多

在前面章节介绍了Jetpack中的Paging的基本使用,在阅读本文前,若不知Paging的基本使用的朋友,可以查看笔者之前的文章Android Jetpack架构组件-Paging介绍及实践

知道了Paging的基本使用,但并不满足实际开发,虽然Paging可以实现分页加载,但Paging在数据请求的时,只要有一次返回的数据为空及PagedList为空,则再不会进行分页

这显然是不友好的,因为返回数据为空有多种原因,可能是网络或者查询数据格式等,返回的PageList为空,这个时候如果将分页结束掉,则显然不能接受;

或者Paging实现的分页加载,如果滑动很快的话,则会出现加载明显卡顿的效果,且无任何友好UI效果展示,如下图所示:

a0fd7efd49b9

卡顿加载更多.gif

在实际开发中,我们希望是慢慢滑动的时候,Paging帮我们处理分页逻辑,而当快速滑动的时候,我们自己接管Paging的分页加载逻辑,出现加载更多的loading,如下效果所示:

a0fd7efd49b9

加载更多有自定义加载动画

接下来,按照上面需求,实现当正常慢慢活动的时候,Paging帮我们分页,当快速滑动的时候,则我们接管Paging的分页加载,

当然监听RecycleView加载更多的视图的有很多种方法,这里直接使用SmartRefreshLayout

在开始之前,先通过ViewModel+DataSource+PagingListAdapter将数据绑定到RecycleView上,若看过之前的基本使用,则以下基本使用部分可以略过

Paging的基本使用

1、先将Paging的基本使用及数据加载完成,则Activity中的代码如下所示:

package com.onexzgj.inspur.pageingsample.pagingpro;

public class PagingProActivity extends AppCompatActivity implements OnRefreshListener, OnLoadMoreListener {

@SuppressLint("RestrictedApi")

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_paging_pro);

recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));

adapter = new PagingProAdapter(this);

recyclerView.setAdapter(adapter);

paingProViewModel = new ViewModelProvider.NewInstanceFactory().create(PaingProViewModel.class);

paingProViewModel.getPageData().observe(this, new Observer>() {

@Override

public void onChanged(PagedList articles) {

submitList(articles);

}

});

}

public void submitList(PagedList result) {

if (result.size() > 0) {

adapter.submitList(result);

}

}

}

2、再来看看PaingProViewModel中的实现

package com.onexzgj.inspur.pageingsample.pagingpro;

/**

* author:onexzgj

* time:2020/5/4

*/

public class PaingProViewModel extends AbsPagingProViewModel {

private AtomicBoolean loadAfter = new AtomicBoolean(false);

private int mPageIndex = 0;

public int getmPageIndex() {

return mPageIndex;

}

@Override

protected DataSource createDataSource() {

return new ArticleDataSource();

}

class ArticleDataSource extends PageKeyedDataSource {

@Override

public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) {

loadData(0, callback, null);

}

@Override

public void loadBefore(@NonNull LoadParams params, @NonNull LoadCallback callback) {

callback.onResult(Collections.emptyList(), 0);

}

@Override

public void loadAfter(@NonNull LoadParams params, @NonNull LoadCallback callback) {

loadData(params.key, null, callback);

}

}

//简单的请求网络业务逻辑

@SuppressLint("RestrictedApi")

private void loadData(int pageIndex, PageKeyedDataSource.LoadInitialCallback initCallback, PageKeyedDataSource.LoadCallback callback) {

mPageIndex = pageIndex;

if (pageIndex > 0) {

loadAfter.set(true);

}

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder().url("https://www.wanandroid.com/article/list/" + pageIndex + "/json").build();

try {

Response response = null;

response = client.newCall(request).execute();

if (response.isSuccessful()) {

ResponseArticle responseArticle = JSON.parseObject(response.body().string(), ResponseArticle.class);

if (initCallback != null) {

initCallback.onResult(responseArticle.getData().getDatas(), pageIndex - 1, pageIndex + 1);

} else {

callback.onResult(responseArticle.getData().getDatas(), pageIndex + 1);

}

if (pageIndex > 0) {

//通过BoundaryPageData发送数据 告诉UI层 是否应该主动关闭上拉加载分页的动画

((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0);

loadAfter.set(false);

}

mPageIndex = pageIndex + 1;

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

3、通过PagedListAdapter将数据绑定到RecycleView上

import com.onexzgj.inspur.pageingsample.R;

/**

* author:onexzgj

* time:2020/5/4

*/

public class PagingProAdapter extends PagedListAdapter {

public Context mContext;

protected PagingProAdapter(Context context) {

super(new DiffUtil.ItemCallback() {

@Override

public boolean areItemsTheSame(@NonNull ResponseArticle.DataBean.Article oldItem, @NonNull ResponseArticle.DataBean.Article newItem) {

return oldItem == newItem;

}

@Override

public boolean areContentsTheSame(@NonNull ResponseArticle.DataBean.Article oldItem, @NonNull ResponseArticle.DataBean.Article newItem) {

return oldItem.getId() == newItem.getId();

}

});

this.mContext= context;

}

@NonNull

@Override

public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

View itemView = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);

return new ViewHolder(itemView);

}

@Override

public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

holder.bindData(getItem(position));

}

class ViewHolder extends RecyclerView.ViewHolder {

private TextView nameView;

public ViewHolder(@NonNull View itemView) {

super(itemView);

nameView = itemView.findViewById(R.id.tv_info);

}

public void bindData(ResponseArticle.DataBean.Article item) {

nameView.setText(item.getTitle());

}

}

}

到这里Paging的基本使用则已经完成,接下来,我们将实现手动接管Paging的上拉加载与下来刷新

实现上拉加载和下拉刷新

1、通过SmartRefreshLayout,来监听RecycleView的下拉刷新与上拉加载更多的监听,如何使用SmartRefreshLayout这里不做详述

...

smartRefreshLayout.setEnableRefresh(true);

smartRefreshLayout.setEnableLoadMore(true);

smartRefreshLayout.setOnRefreshListener(this);

smartRefreshLayout.setOnLoadMoreListener(this);

...

2、刷新逻辑实现

通过实现smartRefreshLayout的onRefresh(),将DataSource重新初始化一下即可,即如下所示:

@Override

public void onRefresh(@NonNull RefreshLayout refreshLayout) {

paingProViewModel.getDataSource().invalidate();

}

3、 加载更多逻辑实现

通过实现smartRefreshLayout的loadMore()中的实现逻辑,如下所示:

@Override

public void onLoadMore(@NonNull RefreshLayout refreshLayout) {

//若列表数据为空,则不触发上拉加载更多数据

final PagedList currentList = adapter.getCurrentList();

if (currentList == null || currentList.size() <= 0) {

finishRefresh(false);

return;

}

//需要注意这里,在PaingProViewModel中自实现loadAfter方法,实现请求分页数据的逻辑

paingProViewModel.loadAfter(paingProViewModel.getmPageIndex(),new PageKeyedDataSource.LoadCallback(){

@Override

public void onResult(@NonNull List data, @Nullable Integer adjacentPageKey) {

PagedList.Config config = currentList.getConfig();

if (data != null && data.size() > 0) {

//这里 咱们手动接管 分页数据加载的时候 使用MutableItemKeyedDataSource也是可以的。

//由于当且仅当 paging不再帮我们分页的时候,我们才会接管。所以 就不需要ViewModel中创建的DataSource继续工作了,所以使用新的DataSource对象,这里是MutablePageKeyedDataSource

MutablePageKeyedDataSource dataSource = new MutablePageKeyedDataSource();

//这里要把列表上已经显示的先添加到dataSource.data中

//而后把本次分页回来的数据再添加到dataSource.data中

dataSource.data.addAll(currentList);

dataSource.data.addAll(data);

PagedList pagedList = dataSource.buildNewPagedList(config);

submitList(pagedList);

}

}

});

}

可以看到我们通过,在PaingProViewModel中定义loadAfter方法,实现接管Paging分页加载的请求数据逻辑,

4、实现PaingProViewModel中的自定义的方法loadAfter()

@SuppressLint("RestrictedApi")

public void loadAfter(int pageIndex, PageKeyedDataSource.LoadCallback callback) {

Log.d("TAG", "loadAfter: pageIndex" + pageIndex);

//是否加载更多的表示位

if (loadAfter.get()) {

callback.onResult(Collections.emptyList(), 0);

return;

}

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder().url("https://www.wanandroid.com/article/list/" + pageIndex + "/json").build();

ArchTaskExecutor.getIOThreadExecutor().

execute(new Runnable() {

@Override

public void run() {

try {

Response response = null;

response = client.newCall(request).execute();

if (response.isSuccessful()) {

ResponseArticle responseArticle = JSON.parseObject(response.body().string(), ResponseArticle.class);

callback.onResult(responseArticle.getData().getDatas(), pageIndex + 1);

if (pageIndex > 0) {

//通过BoundaryPageData发送数据 告诉UI层 是否应该主动关闭上拉加载分页的动画

((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0);

loadAfter.set(false);

}

mPageIndex = pageIndex + 1;

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

);

}

loadAfter为设置是否是Paging上拉加载的标记位,只有Paging进行过上拉加载的时候,才接管上拉加载,即加载的页码大于0的时候才接管,否则返回空的PagedList即可。

5、自定义的MutablePageKeyedDataSource的实现

package com.onexzgj.inspur.pageingsample.pagingpro;

@SuppressLint("RestrictedApi")

public class MutablePageKeyedDataSource extends PageKeyedDataSource {

public List data = new ArrayList<>();

public PagedList buildNewPagedList(PagedList.Config config) {

PagedList pagedList = new PagedList.Builder(this, config)

.setFetchExecutor(ArchTaskExecutor.getIOThreadExecutor())

.setNotifyExecutor(ArchTaskExecutor.getMainThreadExecutor())

.build();

return pagedList;

}

@Override

public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) {

callback.onResult(data, null, null);

}

@Override

public void loadBefore(@NonNull LoadParams params, @NonNull LoadCallback callback) {

callback.onResult(Collections.emptyList(), null);

}

@Override

public void loadAfter(@NonNull LoadParams params, @NonNull LoadCallback callback) {

callback.onResult(Collections.emptyList(), null);

}

}

作用相当于重新创建一个新的DataSource,且绑定数据集合构建出一个PagedList对象,供Paging使用。

总结

到这里,Paging自定义上拉加载更多介绍完了,建档总结,即通过SmartRefreshLayout监听RecycleView的loadMore方法,通过在ViewModel中自定义loadAfter来加载数据,且重新创建DataSource和将集合数据List,和重新构建出一个PageList即可,文章中的示例代码已上Jetpack/pagingpro

该仓库为演示Jetpack的组件的仓库,分别对Lifecyele、LiveData、ViewModel、Room、WorkManager、Paging的介绍和使用

详细介绍文章

项目目录结构为如下

a0fd7efd49b9

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值