- Jetpack series
- Android Jetpack architecture component - Lifecycle usage
- Android Jetpack architecture component - LiveData usage
- The use and principle of ViewModel, an architecture component of Android Jetpack
- Android Jetpack architecture component - Paging introduction and Practice
- Android Jetpack architecture component - Room basic use
- Android Jetpack architecture component Room database query Art
- Android Jetpack architecture component Room upgrade
- Android Jetpack architecture component - WorkManager usage
- Android Jetpack Architecture - Paging custom pull-up loading more
In the previous chapter, I introduced the basic use of Paging in Jetpack. Before reading this article, if you don't know the basic use of Paging, you can check my previous articles Android Jetpack architecture component - Paging introduction and Practice
Knowing the basic use of Paging, but it does not meet the actual development. Although Paging can realize Paging loading, when Paging requests data, as long as the data returned once is empty and PagedList is empty, Paging will not be performed again
This is obviously unfriendly, because there are many reasons why the returned data is empty, such as network or query data format, etc., and the returned PageList is empty. If the paging is ended at this time, it is obviously unacceptable;
Or the Paging loading implemented by Paging, if it slides quickly, will show the effect of loading the graphics card, and there is no friendly UI effect, as shown in the following figure:
In actual development, we hope that Paging can help us deal with Paging logic when it is sliding slowly, while when it is sliding quickly, we take over Paging loading logic ourselves, and more loading will occur, as shown in the following effect:
Next, according to the above requirements, Paging helps us page when we are normal and slowly active. When we are fast sliding, we take over the page loading of Paging,
In the example of Jetpack
ViewModel,DataSource,Paging,PagingListAdapter And cooperate SmartRefreshLayout To complete pull-up loading and pull-down refresh
- Of course, there are many ways to listen to RecycleView to load more views. Here you can directly use SmartRefreshLayout
Before you start, use ViewModel+DataSource+PagingListAdapter to bind data to RecycleView. If you have seen the previous Basic use , the following basic usage parts can be skipped
Basic use of Paging
- 1. After the basic usage and data loading of Paging are completed, the code in Activity is as follows:
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. Let's take a look at the implementation in PaingProViewModel
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); } } //Simple request network business logic @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) { //Send data through BoundaryPageData to tell the UI layer whether it should actively turn off the animation of pull-up loading pages ((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0); loadAfter.set(false); } mPageIndex = pageIndex + 1; } } catch (IOException e) { e.printStackTrace(); } } }
- 3. Data binding to RecycleView via PagedListAdapter
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()); } } }
The basic use of Paging has been completed. Next, we will manually take over the pull-up loading and down refreshing of Paging
Implement pull-up loading and pull-down refresh
- 1. Through SmartRefreshLayout , to monitor the pull-down refresh and pull-up load of RecycleView. How to use SmartRefreshLayout is not detailed here
... smartRefreshLayout.setEnableRefresh(true); smartRefreshLayout.setEnableLoadMore(true); smartRefreshLayout.setOnRefreshListener(this); smartRefreshLayout.setOnLoadMoreListener(this); ...
- 2. Refresh logic implementation
By implementing onRefresh() of smartRefreshLayout, reinitialize the DataSource as follows:
@Override public void onRefresh(@NonNull RefreshLayout refreshLayout) { paingProViewModel.getDataSource().invalidate(); }
- 3. Load more logical implementations
The implementation logic in loadMore() of smartRefreshLayout is implemented as follows:
@Override public void onLoadMore(@NonNull RefreshLayout refreshLayout) { //If the list data is empty, pull-up will not be triggered to load more data final PagedList<ResponseArticle.DataBean.Article> currentList = adapter.getCurrentList(); if (currentList == null || currentList.size() <= 0) { finishRefresh(false); return; } //It should be noted that in PaingProViewModel, the loadAfter method is implemented to realize the logic of paging data request 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) { //Here we can also use mutableitemkeyedatasource when we manually take over paging data loading. //Because we take over when and only when paging doesn't page us anymore. So we don't need the DataSource created in ViewModel to continue working, so we use the new DataSource object, MutablePageKeyedDataSource MutablePageKeyedDataSource dataSource = new MutablePageKeyedDataSource(); //Here, you need to add what is already shown in the list to dataSource.data first //Then add the data from this paging back to dataSource.data dataSource.data.addAll(currentList); dataSource.data.addAll(data); PagedList pagedList = dataSource.buildNewPagedList(config); submitList(pagedList); } } }); }
You can see that we can take over the request data logic of Paging by defining the loadAfter method in the PaingProViewModel,
- 4. Implement the custom method loadAfter() in PaingProViewModel
@SuppressLint("RestrictedApi") public void loadAfter(int pageIndex, PageKeyedDataSource.LoadCallback<Integer, ResponseArticle.DataBean.Article> callback) { Log.d("TAG", "loadAfter: pageIndex" + pageIndex); //Whether to load more representation bits 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) { //Send data through BoundaryPageData to tell the UI layer whether it should actively turn off the animation of pull-up loading pages ((MutableLiveData) getBoundaryPageData()).postValue(responseArticle.getData().getDatas().size() > 0); loadAfter.set(false); } mPageIndex = pageIndex + 1; } } catch (IOException e) { e.printStackTrace(); } } } ); }
loadAfter is to set whether it is the flag bit of Paging pull-up loading. Pull-up loading is only taken over when Paging has done pull-up loading, that is, when the loaded page number is greater than 0, it is taken over. Otherwise, an empty PagedList is returned.
- 5. Implementation of custom MutablePageKeyedDataSource
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); } }
It is equivalent to re creating a new DataSource, and binding the data collection to build a PagedList object for Paging to use.
summary
Here, more introduction of Paging custom pull-up loading is finished, and the summary of file creation is made. That is to listen to the loadMore method of RecycleView through SmartRefreshLayout, load data by customizing loadAfter in ViewModel, recreate DataSource, List collection data, and rebuild a PageList. The sample code in this article has been listed Jetpack/pagingpro
This warehouse is a warehouse to demonstrate the components of Jetpack. It introduces and uses Lifecyele, LiveData, ViewModel, Room, WorkManager and Paging respectively
##Detailed introduction
-
The use and principle of ViewModel, an architecture component of Android Jetpack
-
Android Jetpack architecture component - Paging introduction and Practice
-
Android Jetpack architecture component Room database query Art