一、Paging简介
在Android利用开发中,咱们常常须要以列表的形式来展现大量的数据,这些数据可能来自网路,也能够来自本地的数据库。为了防止一次性加载大量的数据,对数据进行分页就显得很有必要。分页加载能够依据须要对数据进行按需加载,在不影响用户体验的前提下,晋升利用的性能。
为了不便开发者进行分页解决,Google为开发者提供了分页组件(Paging),借助Paging组件开发者能够轻松的加载和出现大型数据集,同时在 RecyclerView 中进行疾速、有限滚动。并且,它能够从本地存储和/或网络加载分页数据,并让开发者可能定义内容的加载形式,同时它还反对与Room、LiveData 和 RxJava组合应用。
1.1 反对的架构类型
目前,Paging能够反对3种架构类型,别离是网路、数据、网路和数据库,架构的示意图如下所示。
网路
在Android利用开发中,对网路数据进行分页加载是一种比拟常见的场景,也是咱们平时开发中遇到得最多的。不同公司对分页机制所波及的API接口通常会不一样,但总体而言,能够分为3中。针对这些不同的分类,Paging提供了PageKeyedDataSource、PositionalDataSource和ItemKeyedDataSource。
- PageKeyedDataSource:依据传入的页面num获取某一页的数据,比方获取第2页的数据。
- PositionalDataSource:分页时默认显示的第几页。
- ItemKeyedDataSource:申请下一页的关键字。
数据库
除了网路外,数据源来源于数据库的场景也十分多,如果曾经把握了对网路数据的分页,那么对数据库的数据进行分页天然非常简略,只不过数据源的读取形式不同而已。
网路+数据库
在这种场景中,咱们会对网路的数据进行缓存,而数据库就是比拟场景的一种数据长久化形式,比方聊天利用中。首先,咱们会利用数据库对网路数据进行缓存,不过在这种场景下,咱们须要同时解决数据库和网路两个数据源,因而须要约定好网路和数据库的数据处理逻辑。
1.2 工作流程
在正式应用Paging之前,咱们须要对Paging的工作流程有一个大抵的理解。如下图所示,是应用Paging须要经验的几个步骤。
如上图所示,次要的步骤如下:
- 应用DataSource从服务器获取或者从本地数据库获取数据。
- 将数据保留到PageList中。
- 将PageList的数据交给PageListAdapter。
- PageListAdapter在后盾线程比照原来的PageList和新的PageList,生成新的PageList。
- PageListAdapter告诉RecyclerView进行数据的更新。
1.3 外围概念
应用Paging库进行分页加载时,须要用到几个外围的类,别离是PagedListAdapter、PageList和DataSource。
PagedListAdapter
家喻户晓,在Android列表开发中须要应用RecyclerView,并且须要配合自定义Adapter。PagedListAdapter继承于RecyclerView.Adapter,这表明它也是一个RecyclerView.Adapter,并且扩大了RecyclerView.Adapter的反对异步差分更新性能。
PageList
PageList是用于告诉DataSource何时获取数据,以及如何获取数据。比方,何时获取第一页数据,以及何时开始加载数据期待。并且,DataSource数据源都将通过PageList设置给PagedListAdapter。
DataSource
DataSource次要用于执行数据的加载操作,并且数据的载入须要在子线程中进行,否则会造成主线程的阻塞。DataSource的起源能够是网路,也能够是本地的数据库,如Room。依据分页机制的不同,DataSource能够有3种起源,别离是PageKeyedDataSource、PositionalDataSource和ItemKeyedDataSource。
- PageKeyedDataSource:依据传入的页面num获取某一页的数据,比方获取第2页的数据。
- PositionalDataSource:分页时默认显示的第几页。
- ItemKeyedDataSource:申请下一页的关键字。
二、根本应用
2.1 增加依赖
首先,在app的build.gradle文件中增加Paging组件库的依赖,如下所示。
dependencies { //网路申请库 implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.1' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.squareup.okhttp3:okhttp:4.8.0' implementation 'com.squareup.okio:okio:2.7.0' //Paging def paging_version = "2.1.0" implementation "androidx.paging:paging-runtime:$paging_version" }
2.2 定义网路申请
在Android开发中,数据通常来源于网路,咱们能够应用retrofit实现网络数据的申请。在获取数据之前,咱们须要先新建一个数据实体类,次要用来存储获取的数据,如下所示是应用干货集中营的开源 Api 的数据的实体类。
public class DataBean { private int count; private boolean error; private List<ResultsBean> results; ...//省略get和set public static class ResultsBean { private String desc; private String ganhuo_id; private String publishedAt; private String readability; private String type; private String url; private String who; ... //省略get和set }}
而后,为了实现网路申请,咱们须要依照Retrofit的应用形式新建一个Api,用于对立治理申请接口,如下所示。
public interface Api { //开源API:http://gank.io/api/search/query/listview/category/Android/count/10/page/1 @GET("api/search/query/listview/category/Android/count/10/page/{page}") Call<List<DataBean.ResultsBean>> getArticleList1(@Path("page") int page);}
而后,咱们对Retrofit进行一个简略的封装,而后用它实现网路申请,如下所示。
public class RetrofitClient { private static RetrofitClient instance; private Retrofit mRetrofit; private OkHttpClient getClient(){ OkHttpClient.Builder builder = new OkHttpClient().newBuilder() .connectTimeout(30, TimeUnit.SECONDS)//设置超时工夫 .readTimeout(10, TimeUnit.SECONDS)//设置读取超时工夫 .writeTimeout(10, TimeUnit.SECONDS);//设置写入超时工夫 return builder.build(); } public RetrofitClient() { mRetrofit=new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .client(getClient()) .build(); } public static RetrofitClient getInstance() { if (instance==null){ instance=new RetrofitClient(); } return instance; } public <T> T createApi(Class<T> cls){ T t=mRetrofit.create(cls); return t; }}
2.3 创立数据源
如果应用的是网路数据,应用Paging进行分页加载时须要自定义DataSource。后面说过,DataSource有3个抽象类,别离是PageKeyedDataSource、PositionalDataSource和ItemKeyedDataSource,因而理论应用时须要继承他们。
因为此处加载的是网络数据,所以应用PageKeyedDataSource更适合,咱们新建一个继承自PageKeyedDataSource的自定义DataSource,如下所示。
public class PagingDataSource extends PageKeyedDataSource<String, DataBean.ResultsBean> { private static final String TAG = PagingDataSource.class.getSimpleName(); private int mPage = 1; @Override public void loadInitial(@NonNull LoadInitialParams<String> params, @NonNull final LoadInitialCallback<String, DataBean.ResultsBean> callback) { Api api = RetrofitClient.getInstance().createApi(Api.class); Call<List<DataBean.ResultsBean>> call = api.getArticleList1(mPage); call.enqueue(new Callback<List<DataBean.ResultsBean>>() { @Override public void onResponse(Call<List<DataBean.ResultsBean>> call, Response<List<DataBean.ResultsBean>> response) { if (response.isSuccessful() && response.code() == 200) { List<DataBean.ResultsBean> data = response.body(); callback.onResult(data, "before", "after"); } } @Override public void onFailure(Call<List<DataBean.ResultsBean>> call, Throwable t) { Log.e(TAG, "--onFailure-->" + t.getMessage()); } }); } //加载上一页 @Override public void loadBefore(@NonNull LoadParams<String> params, @NonNull LoadCallback<String, DataBean.ResultsBean> callback) { Log.i(TAG, "--loadBefore-->" + params.key); } //加载下一页 @Override public void loadAfter(@NonNull final LoadParams<String> params, @NonNull final LoadCallback<String, DataBean.ResultsBean> callback) { mPage++; Api api = RetrofitClient.getInstance().createApi(Api.class); Call<List<DataBean.ResultsBean>> call = api.getArticleList1(mPage); call.enqueue(new Callback<List<DataBean.ResultsBean>>() { @Override public void onResponse(Call<List<DataBean.ResultsBean>> call, Response<List<DataBean.ResultsBean>> response) { if (response.isSuccessful() && response.code() == 200) { List<DataBean.ResultsBean> data = response.body(); callback.onResult(data, params.key); } } @Override public void onFailure(Call<List<DataBean.ResultsBean>> call, Throwable t) { Log.i(TAG, "--onFailure-->" + t.getMessage()); } }); }}
在下面的代码中,PageKeyedDataSource须要重写三个办法。
- loadInitial():第一次申请数据,即初始化状态时申请数据。
- loadBefore():申请上一页数据,根本没有用途。
- loadAfter(): 申请下一页数据。
DataSource创立好了,再创立一个DataSource.Factory,返回对应的DataSource实例,如下所示。
public class PagingDataSourceFactory extends DataSource.Factory<String, DataBean.ResultsBean> { @NonNull @Override public DataSource<String, DataBean.ResultsBean> create() { PagingDataSource dataSource = new PagingDataSource(); return dataSource; }}
2.4 配置分页参数
在Jetpack的架构外面,官网举荐每个页面持有一个ViewModel对象,以保证数据的正确性以及防止其余的问题产生。
在在 ViewModel 中创立 PagedList.Config 并进行分页参数配置,创立 DataSource 工厂对象,最终生成反对分页的 LiveData 数据。要想创立LiveData,须要先创立一个LivePagedListBuilder,LivePagedListbuilder有设分页数量和配置参数两种办法,如下所示。
public class PagingViewModel extends ViewModel { private int pageSize = 20; //PagedList配置 private PagedList.Config config = new PagedList.Config.Builder() .setInitialLoadSizeHint(pageSize)//设置首次加载的数量 .setPageSize(pageSize)//设置每页加载的数量 .setPrefetchDistance(2)//设置间隔每页最初数据项来时预加载下一页数据 .setEnablePlaceholders(false)//设置是否启用UI占用符 .build(); //DataSource.Factory private DataSource.Factory<String,DataBean.ResultsBean> factory = new PagingDataSourceFactory(); //LiveData private LiveData<PagedList<DataBean.ResultsBean>> mPagedList = new LivePagedListBuilder<>(factory, config) .build(); public LiveData<PagedList<DataBean.ResultsBean>> getPagedList() { return mPagedList; }}
下面代码中,咱们提到了占位符,占位符的作用是在数据实现渲染之前,向用户显示的默认视图成果。占位符具备以下长处:
- 反对滚动条:PagedList 可向 PagedListAdapter 提供列表项数量。此信息容许适配器绘制滚动条来传播整个列表大小。有新页面载入时,滚动条不会跳到指定地位,因为列表不会扭转大小。
- 无需加载旋转图标:因为列表大小已知,因而无需揭示用户正在加载更多项。
不过,在增加对占位符的反对之前,请留神以下前提条件:
- 须要可计数的数据集:Room 持久性库 中的 DataSource 实例能够无效地计算项的数量。但如果您应用的是自定义本地存储解决方案或网络专用数据架构,确定数据集蕴含多少项可能会开销极大,甚至根本无法确定。
- 适配器必须思考未加载的项:为筹备列表以应答增长而应用的适配器或出现机制须要解决 Null 列表项。例如,将数据绑定到 ViewHolder 时,您须要提供默认值来示意未加载数据。
- 须要同样大小的项视图:如果列表项大小会随着内容而变(例如社交网络更新),则项之间的穿插突变成果并不现实。在这种状况下,咱们强烈建议停用占位符。
2.5 创立PagedListAdapter
PagedListAdapter是一个非凡的RecyclerView的RecyclerAdapter,应用办法也和RecyclerAdapter的应用形式相似,如下所示。
public class PagingAdapter extends PagedListAdapter<DataBean.ResultsBean, PagingAdapter.ViewHolder> { public PagingAdapter() { super(itemCallback); } @NonNull @Override public PagingAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycleview, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull PagingAdapter.ViewHolder holder, int position) { DataBean.ResultsBean bean = getItem(position); if (bean != null) { holder.desc.setText(bean.getDesc()); holder.time.setText(bean.getPublishedAt()); holder.type.setText(bean.getType()); holder.auth.setText(bean.getWho()); } } public class ViewHolder extends RecyclerView.ViewHolder{ TextView desc; TextView time; TextView type; TextView auth; public ViewHolder(View itemView) { super(itemView); desc = itemView.findViewById(R.id.desc); time = itemView.findViewById(R.id.time); type = itemView.findViewById(R.id.type); auth = itemView.findViewById(R.id.auth); } } private static DiffUtil.ItemCallback<DataBean.ResultsBean> itemCallback = new DiffUtil.ItemCallback<DataBean.ResultsBean>() { @Override public boolean areItemsTheSame(@NonNull DataBean.ResultsBean oldItem, @NonNull DataBean.ResultsBean newItem) { return oldItem.getGanhuo_id().equals(newItem.getGanhuo_id()); } @SuppressLint("DiffUtilEquals") @Override public boolean areContentsTheSame(@NonNull DataBean.ResultsBean oldItem, @NonNull DataBean.ResultsBean newItem) { return oldItem.equals(newItem); } };}
在应用PagedListAdapter时,PagedListAdapter外部默认实现DiffUtil来进行数据的差量计算,所以咱们在构造方法外面传递一个DiffUtil.ItemCallback。
2.6 加载分页数据
通过下面的解决后,接下来只须要在Activity中进行数据的申请和绑定即可,如下所示。
public class MainActivity extends AppCompatActivity { private final String TAG = "MainActivity"; private ActivityMainBinding activityMainBinding; private PagingViewModel mViewModel; private PagingAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); initRecyclerView();// getData(); } private void initRecyclerView() { adapter = new PagingAdapter(); LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); activityMainBinding.recycle.setLayoutManager(layoutManager); activityMainBinding.recycle.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); activityMainBinding.recycle.setAdapter(adapter); ViewModelProvider mViewModelProvider = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())); mViewModel = mViewModelProvider.get(PagingViewModel.class); } public void getData() { mViewModel.getPagedList().observe(this, new Observer<PagedList<DataBean.ResultsBean>>() { @Override public void onChanged(PagedList<DataBean.ResultsBean> dataBeans) { adapter.submitList(dataBeans); } }); }}
咱们应用 LiveData 监听加载的数据,而后应用 sumbitList 将数据提交给 PagedListAdapter,PagedListAdapter会在后盾线程中比照新旧数据的差别,最初更新 RecyclerView。
三、Paging3
3.1 概述
Paging是JetPack框架提供的一个分页库,它能够帮忙开发者从本地存储或通过网络加载显示数据,不过因为历史起因,晚期的Paging存在各种应用上的问题,因而Android在前面提供了Paging3用来替换晚期的Paging2。相比Paging2,Paging3有如下一些长处。
- 在内存中缓存分页数据,确保 App 在应用分页数据时无效地应用系统资源。
- 内置删除反复数据的申请,确保 App 无效地应用网络带宽和系统资源。
- 可配置 RecyclerView 的 Adapters,当用户滚动到加载数据的开端时主动申请数据。
- 反对 Kotlin 协程和 Flow, 以及 LiveData 和 RxJava。
- 内置的错误处理反对,包含刷新和重试等性能。
3.1.1 次要变动
在 Paging3 之前,Paging提供了 ItemKeyedDataSource、PageKeyedDataSource、PositionalDataSource 这三个类来进行数据获取的操作。
- PositionalDataSource:用于加载数据无限的数据,比方加载本地数据库。
- PageKeyedDataSource:用来申请网络数据,实用于通过页码分页来申请数据。
- ItemKeyedDataSource:用来申请网络数据,它实用于通过以后页面最初一条数据的 id作为下一页的数据的开始的地位的场景。
在 Paging3 之后,ItemKeyedDataSource、PageKeyedDataSource、PositionalDataSource 合并为一个 PagingSource,所有旧 API 加载办法被合并到 PagingSource 中的单个 load() 办法中,如下所示。
abstract suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value>
除此之外,变动的内容还包含:
- LivePagedListBuilder 和 RxPagedListBuilder 合并为了 Pager。
- 应用 PagedList.Config 替换 PagingConfig。
- 应用 RemoteMediator 替换了 PagedList.BoundaryCallback 去加载网络和本地数据库的数据。
3.1.2 重要概念
在正式学习Paging3之前,咱们须要弄清楚几个重要的概念:
- PagingSource:繁多的数据源,能够示意数据库申请失去的数据,也能够是网络申请失去的数据。
- RemoteMediator:繁多的数据源,它会在 PagingSource 没有数据的时候,再应用 RemoteMediator 提供的数据,如果既存在数据库申请又存在网络申请,通常 PagingSource 用于进行数据库申请,RemoteMediator 进行网络申请。
- PagingData:单次分页数据的容器。
- Pager:用来构建 Flow<
PagingData
> 的类,实现数据加载实现的回调。 - PagingDataAdapter:分页加载数据的 RecyclerView 的适配器。
总的来说,应用Paging3加载网络数据的流程是:PagingSource 和 RemoteMediator 充当数据源的角色,ViewModel 应用 Pager 中提供的 Flow<PagingData
> 监听数据刷新,每当 RecyclerView 行将滚动到底部的时候,就会加载新的数据,最初再应用PagingAdapter 展现数据。
3.1.3 Paging3利用架构
上面是Android官网举荐的接入 Paging3的利用架构图。
能够发现,应用Paging3实现数据分页时次要蕴含3个对象:
#### 数据仓库层Repository
Repository层次要应用PagingSource这个分页组件来实现,每个PagingSource对象都对应一个数据源,以及该如何从该数据源中查找数据,PagingSource能够从任何单个数据源比方网络或者数据库中查找数据。
Repository层还有另一个分页组件能够应用RemoteMediator,它是一个分层数据源,比方有本地数据库缓存的网络数据源。
ViewModel层
Repository最终返回一个异步流包裹的PagingDataFlow<PagingData<Value
>>,PagingData存储了数据后果,最终能够应用它将数据跟UI界面关联起来。ViewModel个别都应用LiveData来跟UI层交互,Flow的扩大函数能够间接转换成一个LiveData可察看对象。
UI层
UI层其实就是Activity/Fragment等视图层,次要的作用是给RecycleView设置Adapter,给Adater设置数据。
3.2 根本应用
3.2.1, 增加依赖
首先,在app的build.gradle文件中增加Paging3组件库的依赖,如下所示。
dependencies { ... //Paging3 def paging_version = "3.0.0-alpha11" implementation "androidx.paging:paging-runtime:$paging_version" }
3.2.2 定义数据源
Paging 2提供了三种类型的 PageSource,开发者须要依据应用场景去进行抉择。而Paging 3对数据源进行了对立解决,开发时只须要继承 PagingSource 即可。
Paging 3的数据源能够是PagingSource,也能够是RemoteMediator,它们的区别如下。
- PagingSource:繁多数据源以及如何从该数据源中查找数据,数据源的变动会间接映射到 UI 上。
- RemoteMediator:实现加载网络分页数据并更新到数据库中,然而数据源的变动不能间接映射到 UI 上。
那理论应用时,如何进行抉择呢?PagingSource次要用于加载无限的数据集,而RemoteMediator则次要用来加载网络分页数据,理论应用时须要联合 PagingSource 实现保留更多数据操作并映射到 UI 上。
上面以WanAndroid的接口为例,接口地址为:https://www.wanandroid.com/article/list/1/json,数据源的代码如下。
public class Paging3DataSource extends PagingSource<Integer, ArticleBean.DataBean.DatasBean> { @Nullable @Override public Object load(@NotNull LoadParams<Integer> params, @NotNull Continuation<? super LoadResult<Integer, ArticleBean.DataBean.DatasBean>> continuation) { int page = 0; if(params.getKey()!=null){ page=params.getKey(); } //获取网络数据 ArticleBean result = (ArticleBean) RetrofitClient.getInstance().getApi().getArticleList(page); //须要加载的数据 List<ArticleBean.DataBean.DatasBean> datas= result.getData().getDatas(); //如果能够往上加载更多就设置该参数,否则不设置 String prevKey=null; //加载下一页的key 如果传null就阐明到底了 String nextKey=null; if(result.getData().getCurPage() == result.getData().getPageCount()){ nextKey=null; }else { nextKey=String.valueOf(page+1); } return new LoadResult.Page<>(datas,prevKey,nextKey); }}
在下面的代码中,自定义的PagingSource须要继承自PagingSource,须要传入两个泛型,第一个示意下一页数据的加载形式,另一个是返回数据的实体类。同时,自定义的PagingSource还须要重写load办法来触发异步加载,能够看到它是一个用suspend润饰的挂起函数,能够很不便的应用协程异步加载。
而load办法的参数LoadParams中有一个key值,能够在加载下一页数据时应用。返回值是一个LoadResult,出现异常调用LoadResult.Error(e),失常强开状况下调用LoadResult.Page办法来设置从网络或者数据库获取到的数据。
3.2.3 网络申请
在理论利用开发中,咱们须要从网络中获取数据,而后再进行其余业务操作。网络申请个别会借助Retrofit来实现,上面是应用Retrofit实现WanAndroid接口申请的简略的封装,代码如下。
public class RetrofitClient { private static String BASE_URL="https://www.wanandroid.com/"; private static RetrofitClient instance; private Retrofit mRetrofit; private OkHttpClient getClient(){ HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Log.i("RetrofitClient: ", message); } }); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(interceptor) .connectTimeout(60, TimeUnit.SECONDS) .build(); return client; } public RetrofitClient() { mRetrofit=new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(getClient()) .build(); } public static RetrofitClient getInstance() { if (instance==null){ instance=new RetrofitClient(); } return instance; } public <T> T createApi(Class<T> cls){ T t=mRetrofit.create(cls); return t; }}
而后,在创立一个WanAndroidApi来治理所有的Api接口,如下所示。
public interface WanAndroidApi { @GET("article/list/{pageNum}/json") Call<ArticleBean> getArticleList(@Path("page") int page);}
3.2.4 构建ViewModel
分页数据的容器被称为 PagingData,每次刷新数据时,都会创立一个 PagingData 的实例。如果要创立 PagingData 数据流,那么须要创立一个 Pager 实例,并提供一个 PagingConfig 配置对象和一个能够通知 Pager 如何获取您实现的 PagerSource 的实例的函数,以供 Pager 应用。
理论开发中,Repository返回的是一个异步流包裹的PagingDataFlow<PagingData<Value
>>,PagingData存储了数据后果。而在MVVM中,咱们须要构建ViewModel来实现是LiveData和UI层交互,而ViewModel的Flow的扩大函数能够将间接将PagingSource转换成一个LiveData可察看对象,代码如下。
public class Paging3ViewModel extends ViewModel { PagingConfig pagingConfig = new PagingConfig(20, 3); public LiveData<PagingData<ArticleBean.DataBean.DatasBean>> getArticleData() { CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(this); Pager<Integer, ArticleBean.DataBean.DatasBean> pager = new Pager<>(pagingConfig, () -> new Paging3DataSource()); LiveData<PagingData<ArticleBean.DataBean.DatasBean>> cachedResult=PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope); return cachedResult; }}
3.2.5 创立Adapter
和Paging2的Adapter应用步骤相似,在Paging3中创立Adapter须要继承 PagingDataAdapter,而后提供 DiffUtil.ItemCallback<T
>,如下所示。
public class Paging3Adapter extends PagingDataAdapter<ArticleBean.DataBean.DatasBean, Paging3Adapter.ViewHolder> { public Paging3Adapter(@NotNull DiffUtil.ItemCallback<ArticleBean.DataBean.DatasBean> diffCallback) { super(itemCallback); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycleview, parent, false); return new Paging3Adapter.ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { ArticleBean.DataBean.DatasBean bean = getItem(position); if (bean != null) { holder.desc.setText(bean.getDesc()); holder.time.setText(String.valueOf(bean.getPublishTime())); holder.type.setText(bean.getType()); holder.auth.setText(bean.getAuthor()); } } public static class ViewHolder extends RecyclerView.ViewHolder{ TextView desc; TextView time; TextView type; TextView auth; public ViewHolder(View itemView) { super(itemView); desc = itemView.findViewById(R.id.desc); time = itemView.findViewById(R.id.time); type = itemView.findViewById(R.id.type); auth = itemView.findViewById(R.id.auth); } } public static DiffUtil.ItemCallback<ArticleBean.DataBean.DatasBean> itemCallback = new DiffUtil.ItemCallback<ArticleBean.DataBean.DatasBean>() { @Override public boolean areItemsTheSame(@NonNull ArticleBean.DataBean.DatasBean oldItem, @NonNull ArticleBean.DataBean.DatasBean newItem) { return oldItem.getId()==newItem.getId(); } @SuppressLint("DiffUtilEquals") @Override public boolean areContentsTheSame(@NonNull ArticleBean.DataBean.DatasBean oldItem, @NonNull ArticleBean.DataBean.DatasBean newItem) { return oldItem.equals(newItem); } };}
能够发现,Paging3Adapter的代码和一般的Adapter的代码是差不多的,也是须要重写onCreateViewHolder和onBindViewHolder两个办法。
3.2.6 在UI 中展现数据
最初,咱们在Activity中应用RecyclerView展现获取的数据即可,如下所示。
public class MainActivity extends AppCompatActivity { private final String TAG = "MainActivity"; private Paging3Adapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView( R.layout.activity_main); initRecyclerView(); } private void initRecyclerView() { adapter = new Paging3Adapter(); RecyclerView recyclerView = findViewById(R.id.recycle); recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); recyclerView.setAdapter(adapter); Paging3ViewModel viewModel=new ViewModelProvider(this).get(Paging3ViewModel.class); viewModel.getArticleData().observe(this, pagingData -> adapter.submitData(getLifecycle(),pagingData)); }}
除此之外,Paging3还反对增加 Header 和 Footer来实现上拉刷新和下拉加载更多的性能。
参考: 应用官网Paging3分页库实现RecyclerView加载更多