上一篇主要介绍了Paging加载库的主要组件,以及组件之间的工作方式,不是很清楚的可以移步上一篇 Jetpack插件化学习之AndroidX Paging 大数据列表加载库一
本篇主要介绍Paging库如何在项目中使用。 开篇之前,本文的数据来源以及demo代码的书写参考了这位大佬的文章。 使用Paging Library获取网络数据 本篇也是直接加载的网络数据。
首先在项目中添加Paging库依赖
// androidx paging implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0" implementation "androidx.paging:paging-runtime:2.1.0-alpha01"依赖中看到有AndoridX的依赖。关于AndoridX的介绍以及Android到AndroidX的升级
接下来按照Paging的组件,依次创建
DataSourcePagedList&LivePagedListProvider初始化一个PositionalDataSource对象并实现相关方法。loadInitial、loadRange。
public class PagingItemDataViewModel extends AndroidViewModel { public static final int CONTENT_LENGTH = 20; public static final int FIRST_PAGE = 1; private int mPage = FIRST_PAGE; // final List<GankData> resultBeans = new ArrayList<>(); private LiveData<PagedList<GankData>> mLiveData; public PagingItemDataViewModel(@NonNull Application application) { super(application); } public LiveData<PagedList<GankData>> getmLiveData() { initPageList(); return mLiveData; } private void initPageList() { final PositionalDataSource<GankData> positionalDataSource = new PositionalDataSource<GankData>() { private int computeCount() { //这里的实际计数代码 return CONTENT_LENGTH; } /** * 网络请求耗时操作 * @param onHttpRequestListener 网络请求成功的回调 * @param page 加载到第几页 */ private void loadRangeInternal(final OnHttpRequestListener onHttpRequestListener, int page) { //这里的实际加载代码 ManageHttp.getWelfare1(new BaseOnResponseCallBack<List<GankData>>(List.class) { @Override public void onNext(List data, int IDUrl, boolean isCache) { onHttpRequestListener.onNext(data, IDUrl, isCache); } @Override public void onStart(int IDUrl, boolean isCache) { } @Override public void onCompleted(int IDUrl, boolean isCache) { } @Override public void onError(Throwable e, int IDUrl, boolean isCache) { } }, page); // return resultBeans; } @Override public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback<GankData> callback) { // 计算一页显示的条目 final int totalCount = computeCount(); // 计算显示到第几条数据 final int position = computeInitialLoadPosition(params, totalCount); final int loadSize = computeInitialLoadSize(params, position, totalCount); //初次初始化 loadRangeInternal(new OnHttpRequestListener() { @Override public void onStart(int IDUrl, boolean isCache) { } @Override public void onNext(Object data, int IDUrl, boolean isCache) { // resultBeans.addAll((Collection<? extends GankData>) data); gankData = (List<GankData>) data; callback.onResult((List<GankData>) data, position, totalCount); } @Override public void onCompleted(int IDUrl, boolean isCache) { } @Override public void onError(Throwable e, int IDUrl, boolean isCache) { } }, mPage); } List<GankData> gankData = new ArrayList<>(); @Override public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback<GankData> callback) { LogUtils.d("PagingItemDataViewModel", "loadRange", "params->=" + params.loadSize, "params->=" + params.startPosition); // 每次加载到翻页条目,page增量 mPage++; loadRangeInternal(new OnHttpRequestListener() { @Override public void onStart(int IDUrl, boolean isCache) { } @Override public void onNext(Object data, int IDUrl, boolean isCache) { // resultBeans.addAll((Collection<? extends GankData>) data); gankData = (List<GankData>) data; // 回调到provider中 callback.onResult((List<GankData>) data); } @Override public void onCompleted(int IDUrl, boolean isCache) { } @Override public void onError(Throwable e, int IDUrl, boolean isCache) { } }, mPage); } }; // 构建LiveData mLiveData = new LivePagedListBuilder(new PageDataSourceFactory(positionalDataSource) , new PagedList.Config.Builder().setPageSize(CONTENT_LENGTH) .setPrefetchDistance(CONTENT_LENGTH) .setEnablePlaceholders(false).setInitialLoadSizeHint(CONTENT_LENGTH) .build()).build(); } }为了使项目的结构更加清晰,将此处的业务逻辑放到ViewModel层。
computeCount:方法用来初始化每一页所展示的数据。
private int computeCount() { //这里的实际计数代码 return CONTENT_LENGTH; }loadRangeInternal:从网络加载数据的方法。 第一个参数onHttpRequestListener网络请求成功的回调接口,page为加载到第几页。
private void loadRangeInternal(final OnHttpRequestListener onHttpRequestListener, int page) ; ****loadInitial:进行第一次页面加载时候的数据加载。
// 计算一页显示的条目 final int totalCount = computeCount(); // 计算显示到第几条数据 final int position = computeInitialLoadPosition(params, totalCount); final int loadSize = computeInitialLoadSize(params, position, totalCount);第一次数据加载完成之后,要把上述的几个变量回调出去,通知当前加载的数据条目数,以及每一页需要加载的条目数。
callback.onResult((List<GankData>) data, position, totalCount);loadRange:滑动列表时候触发数据的请求,并将数据回调出去。
public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback<GankData> callback)LoadRangeParams :包含当前加载的位置position、下一页加载的长度count
LoadRangeCallback:将数据回调给界面使用callback.onResult
LivePagedListProvider通过构造方法将PagedList与DataSource联系在一起。
mLiveData = new LivePagedListBuilder(new PageDataSourceFactory(positionalDataSource) , new PagedList.Config.Builder().setPageSize(CONTENT_LENGTH) .setPrefetchDistance(CONTENT_LENGTH) .setEnablePlaceholders(false).setInitialLoadSizeHint(CONTENT_LENGTH) .build()).build();mLiveData是一个实现了观察者模式的对象。使用该对象post的方法可以将获取的数据返回到UI层,供RecycleView展示。
使用到的DataSource创建类
public class PageDataSourceFactory extends DataSource.Factory { public PositionalDataSource<GankData> mPositionalDataSource; public PageDataSourceFactory(PositionalDataSource<GankData> positionalDataSource) { this.mPositionalDataSource =positionalDataSource; } @Override public DataSource create() { return mPositionalDataSource; } }至此,我们完成了数据的分页逻辑已经完成。回头来看一下里面的代码,主要有loadInitial、loadRange这两个方法。以及初始化LiveData的过程。
接下来,我们看一下在Activity中如何获取数据。
class PagingActivity : BaseMvpActivity<Contract.IView, MainPresenter>(), Contract.IView { override fun initTitle() { } var adapter: MyAdapter? = null var pagingItemDataViewModel: PagingItemDataViewModel? = null override val layoutViewID: Int get() = R.layout.activity_main_paging override fun initPresenter(): MainPresenter { return MainPresenter() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pagingItemDataViewModel = ViewModelProviders.of(this).get(PagingItemDataViewModel::class.java) adapter = MyAdapter(object : DiffUtil.ItemCallback<GankData>() { override fun areItemsTheSame(oldItem: GankData, newItem: GankData): Boolean { return oldItem._id.equals(newItem._id) } override fun areContentsTheSame(oldItem: GankData, newItem: GankData): Boolean { return oldItem.url.equals(newItem.url) } }) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = adapter pagingItemDataViewModel!!.getmLiveData().observe(this, object : Observer<PagedList<GankData>> { override fun onChanged(t: PagedList<GankData>?) { adapter!!.submitList(t) } }) } override fun onViewClick(v: View) { super.onViewClick(v) } }Activity界面中我们只需要关注RecycleView的Adapter的生成。
在上一篇的初识中RecycleView的Adapter需要继承自Paging的PagedListAdapter
public class MyAdapter extends PagedListAdapter<GankData, BaseViewHolder> { private Context mContext; protected MyAdapter(@NonNull DiffUtil.ItemCallback<GankData> diffCallback) { super(diffCallback); } @NonNull @Override public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { mContext = parent.getContext(); return new BaseViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_main, parent, false)); } @Override public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) { TextView tvName = holder.getView(R.id.tv_name); ImageView imageView = holder.getView(R.id.iv); GankData data = getItem(position); tvName.setText(position+""); GlideUtils.loadImage(mContext,imageView,data.getUrl()); } }继承PagedListAdapter,添加构造方法。 可以看到在构造方法中添加以往没有见到的DiffUtil.ItemCallback
protected MyAdapter(@NonNull DiffUtil.ItemCallback<GankData> diffCallback) { super(diffCallback); }该callback用来判断接收的数据是否为同一个数据
BaseViewHolder相关
public class BaseViewHolder extends RecyclerView.ViewHolder { private final SparseArray<View> viewSparseArray; public BaseViewHolder(@NonNull View itemView) { super(itemView); viewSparseArray = new SparseArray<>(); } public <T extends View> T getView(@IdRes int id) { View view = viewSparseArray.get(id); if (null == view) { view = itemView.findViewById(id); viewSparseArray.put(id, view); } return (T) view; } }在Activity中实现DiffUti.ItemCall
adapter = MyAdapter(object : DiffUtil.ItemCallback<GankData>() { override fun areItemsTheSame(oldItem: GankData, newItem: GankData): Boolean { return oldItem._id.equals(newItem._id) } override fun areContentsTheSame(oldItem: GankData, newItem: GankData): Boolean { return oldItem.url.equals(newItem.url) } })在areItemsTheSame、areContentsTheSame中编写比较方式。
说了这么多还没有说到数据获取成功时候如何通知RecycleView的Adapter进行数据的更新。
还记得ViewModel中的LiveData对象吗???一个实现了观察者模式的对象。
pagingItemDataViewModel!!.getmLiveData().observe(this, object : Observer<PagedList<GankData>> { override fun onChanged(t: PagedList<GankData>?) { // 数据更新,通知adapter更新数据 adapter!!.submitList(t) } })一个简单的observe将数据传递给注册了监听的对象。 这与我们以往使用的不一样, adapter!!.submitList(t) 调用的submit方法就是PagedListAdapter中的方法。它来对数据进行更新显示。
结合自己的业务逻辑按照上述的配置即可完成Paing分页库的使用。
有问题,联系我。
xpg@alphathink.org