在前面章節(jié)介紹了Jetpack中的Paging的基本使用稻薇,在閱讀本文前恒傻,若不知Paging的基本使用的朋友余素,可以查看筆者之前的文章Android Jetpack架構(gòu)組件-Paging介紹及實(shí)踐
知道了Paging的基本使用蒙兰,但并不滿足實(shí)際開發(fā)闻鉴,雖然Paging可以實(shí)現(xiàn)分頁(yè)加載茵乱,但Paging在數(shù)據(jù)請(qǐng)求的時(shí),只要有一次返回的數(shù)據(jù)為空及PagedList為空孟岛,則再不會(huì)進(jìn)行分頁(yè)
這顯然是不友好的瓶竭,因?yàn)榉祷財(cái)?shù)據(jù)為空有多種原因,可能是網(wǎng)絡(luò)或者查詢數(shù)據(jù)格式等蚀苛,返回的PageList為空在验,這個(gè)時(shí)候如果將分頁(yè)結(jié)束掉,則顯然不能接受堵未;
或者Paging實(shí)現(xiàn)的分頁(yè)加載腋舌,如果滑動(dòng)很快的話,則會(huì)出現(xiàn)加載明顯卡頓的效果渗蟹,且無任何友好UI效果展示块饺,如下圖所示:
在實(shí)際開發(fā)中赞辩,我們希望是慢慢滑動(dòng)的時(shí)候,Paging幫我們處理分頁(yè)邏輯授艰,而當(dāng)快速滑動(dòng)的時(shí)候辨嗽,我們自己接管Paging的分頁(yè)加載邏輯,出現(xiàn)加載更多的loading淮腾,如下效果所示:
接下來糟需,按照上面需求,實(shí)現(xiàn)當(dāng)正常慢慢活動(dòng)的時(shí)候谷朝,Paging幫我們分頁(yè)洲押,當(dāng)快速滑動(dòng)的時(shí)候,則我們接管Paging的分頁(yè)加載圆凰,
示例以Jetpack中的
ViewModel杈帐、DataSource、Paging专钉、PagingListAdapter并且配合SmartRefreshLayout來完成上拉加載和下拉刷新
- 當(dāng)然監(jiān)聽RecycleView加載更多的視圖的有很多種方法挑童,這里直接使用SmartRefreshLayout
在開始之前,先通過ViewModel+DataSource+PagingListAdapter將數(shù)據(jù)綁定到RecycleView上跃须,若看過之前的基本使用站叼,則以下基本使用部分可以略過
Paging的基本使用
- 1、先將Paging的基本使用及數(shù)據(jù)加載完成,則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<PagedList<ResponseArticle.DataBean.Article>>() {
@Override
public void onChanged(PagedList<ResponseArticle.DataBean.Article> articles) {
submitList(articles);
}
});
}
public void submitList(PagedList<ResponseArticle.DataBean.Article> result) {
if (result.size() > 0) {
adapter.submitList(result);
}
}
}
- 2菇民、再來看看PaingProViewModel中的實(shí)現(xiàn)
package com.onexzgj.inspur.pageingsample.pagingpro;
/**
* author:onexzgj
* time:2020/5/4
*/
public class PaingProViewModel extends AbsPagingProViewModel<ResponseArticle.DataBean.Article> {
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<Integer, ResponseArticle.DataBean.Article> {
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, ResponseArticle.DataBean.Article> callback) {
loadData(0, callback, null);
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, ResponseArticle.DataBean.Article> callback) {
callback.onResult(Collections.emptyList(), 0);
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, ResponseArticle.DataBean.Article> callback) {
loadData(params.key, null, callback);
}
}
//簡(jiǎn)單的請(qǐng)求網(wǎng)絡(luò)業(yè)務(wù)邏輯
@SuppressLint("RestrictedApi")
private void loadData(int pageIndex, PageKeyedDataSource.LoadInitialCallback<Integer, ResponseArticle.DataBean.Article> initCallback, PageKeyedDataSource.LoadCallback<Integer, ResponseArticle.DataBean.Article> 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發(fā)送數(shù)據(jù) 告訴UI層 是否應(yīng)該主動(dòng)關(guān)閉上拉加載分頁(yè)的動(dòng)畫
((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0);
loadAfter.set(false);
}
mPageIndex = pageIndex + 1;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 3大年、通過PagedListAdapter將數(shù)據(jù)綁定到RecycleView上
import com.onexzgj.inspur.pageingsample.R;
/**
* author:onexzgj
* time:2020/5/4
*/
public class PagingProAdapter extends PagedListAdapter<ResponseArticle.DataBean.Article, PagingProAdapter.ViewHolder> {
public Context mContext;
protected PagingProAdapter(Context context) {
super(new DiffUtil.ItemCallback<ResponseArticle.DataBean.Article>() {
@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的基本使用則已經(jīng)完成,接下來玉雾,我們將實(shí)現(xiàn)手動(dòng)接管Paging的上拉加載與下來刷新
實(shí)現(xiàn)上拉加載和下拉刷新
- 1翔试、通過SmartRefreshLayout,來監(jiān)聽RecycleView的下拉刷新與上拉加載更多的監(jiān)聽,如何使用SmartRefreshLayout這里不做詳述
...
smartRefreshLayout.setEnableRefresh(true);
smartRefreshLayout.setEnableLoadMore(true);
smartRefreshLayout.setOnRefreshListener(this);
smartRefreshLayout.setOnLoadMoreListener(this);
...
- 2复旬、刷新邏輯實(shí)現(xiàn)
通過實(shí)現(xiàn)smartRefreshLayout的onRefresh(),將DataSource重新初始化一下即可垦缅,即如下所示:
@Override
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
paingProViewModel.getDataSource().invalidate();
}
- 3、 加載更多邏輯實(shí)現(xiàn)
通過實(shí)現(xiàn)smartRefreshLayout的loadMore()中的實(shí)現(xiàn)邏輯驹碍,如下所示:
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
//若列表數(shù)據(jù)為空壁涎,則不觸發(fā)上拉加載更多數(shù)據(jù)
final PagedList<ResponseArticle.DataBean.Article> currentList = adapter.getCurrentList();
if (currentList == null || currentList.size() <= 0) {
finishRefresh(false);
return;
}
//需要注意這里,在PaingProViewModel中自實(shí)現(xiàn)loadAfter方法志秃,實(shí)現(xiàn)請(qǐng)求分頁(yè)數(shù)據(jù)的邏輯
paingProViewModel.loadAfter(paingProViewModel.getmPageIndex(),new PageKeyedDataSource.LoadCallback<Integer, ResponseArticle.DataBean.Article>(){
@Override
public void onResult(@NonNull List<ResponseArticle.DataBean.Article> data, @Nullable Integer adjacentPageKey) {
PagedList.Config config = currentList.getConfig();
if (data != null && data.size() > 0) {
//這里 咱們手動(dòng)接管 分頁(yè)數(shù)據(jù)加載的時(shí)候 使用MutableItemKeyedDataSource也是可以的怔球。
//由于當(dāng)且僅當(dāng) paging不再幫我們分頁(yè)的時(shí)候,我們才會(huì)接管浮还。所以 就不需要ViewModel中創(chuàng)建的DataSource繼續(xù)工作了竟坛,所以使用新的DataSource對(duì)象,這里是MutablePageKeyedDataSource
MutablePageKeyedDataSource dataSource = new MutablePageKeyedDataSource();
//這里要把列表上已經(jīng)顯示的先添加到dataSource.data中
//而后把本次分頁(yè)回來的數(shù)據(jù)再添加到dataSource.data中
dataSource.data.addAll(currentList);
dataSource.data.addAll(data);
PagedList pagedList = dataSource.buildNewPagedList(config);
submitList(pagedList);
}
}
});
}
可以看到我們通過,在PaingProViewModel中定義loadAfter方法担汤,實(shí)現(xiàn)接管Paging分頁(yè)加載的請(qǐng)求數(shù)據(jù)邏輯涎跨,
- 4、實(shí)現(xiàn)PaingProViewModel中的自定義的方法loadAfter()
@SuppressLint("RestrictedApi")
public void loadAfter(int pageIndex, PageKeyedDataSource.LoadCallback<Integer, ResponseArticle.DataBean.Article> 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發(fā)送數(shù)據(jù) 告訴UI層 是否應(yīng)該主動(dòng)關(guān)閉上拉加載分頁(yè)的動(dòng)畫
((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0);
loadAfter.set(false);
}
mPageIndex = pageIndex + 1;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
}
loadAfter為設(shè)置是否是Paging上拉加載的標(biāo)記位崭歧,只有Paging進(jìn)行過上拉加載的時(shí)候隅很,才接管上拉加載,即加載的頁(yè)碼大于0的時(shí)候才接管率碾,否則返回空的PagedList即可叔营。
- 5、自定義的MutablePageKeyedDataSource的實(shí)現(xiàn)
package com.onexzgj.inspur.pageingsample.pagingpro;
@SuppressLint("RestrictedApi")
public class MutablePageKeyedDataSource<Value> extends PageKeyedDataSource<Integer, Value> {
public List<Value> data = new ArrayList<>();
public PagedList<Value> buildNewPagedList(PagedList.Config config) {
PagedList<Value> pagedList = new PagedList.Builder<Integer, Value>(this, config)
.setFetchExecutor(ArchTaskExecutor.getIOThreadExecutor())
.setNotifyExecutor(ArchTaskExecutor.getMainThreadExecutor())
.build();
return pagedList;
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Value> callback) {
callback.onResult(data, null, null);
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Value> callback) {
callback.onResult(Collections.emptyList(), null);
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Value> callback) {
callback.onResult(Collections.emptyList(), null);
}
}
作用相當(dāng)于重新創(chuàng)建一個(gè)新的DataSource所宰,且綁定數(shù)據(jù)集合構(gòu)建出一個(gè)PagedList對(duì)象审编,供Paging使用。
總結(jié)
到這里歧匈,Paging自定義上拉加載更多介紹完了,建檔總結(jié)砰嘁,即通過SmartRefreshLayout監(jiān)聽RecycleView的loadMore方法件炉,通過在ViewModel中自定義loadAfter來加載數(shù)據(jù),且重新創(chuàng)建DataSource和將集合數(shù)據(jù)List矮湘,和重新構(gòu)建出一個(gè)PageList即可斟冕,文章中的示例代碼已上Jetpack/pagingpro
該倉(cāng)庫(kù)為演示Jetpack的組件的倉(cāng)庫(kù),分別對(duì)Lifecyele缅阳、LiveData磕蛇、ViewModel、Room十办、WorkManager秀撇、Paging的介紹和使用