Android Jetpack组件之Paging Library使用篇

553 阅读3分钟

PS:原文首发于微信公众号:躬行之

阅读本文之前,可先阅读同系列 Android Jetpack 组件文章如下 :

本文将介绍 Paging Library 库的使用,其源码解析将在下篇文章中介绍,Paging Library 组件是 Android Jetpack 的一部分,是 Google 退出的官方分页组件,如果项目中使用了 Google 新推出的官方架构组件,如 LiveData、Lifecycle、ViewModel 等,就可以尝试将该分页组件引入自己的项目,它的优点是无痕加载更多数据,一定程度上提高用户体验。

简述一下使用 pageing 组件分页加载数据的过程,DataSource 负责从网络或数据库加载数据,将数据存储在 PagedList 中,使用 submitList 提交数据到 PagedListAdapter,当数据发生变化时会在后台线程中计算数据差异,最后 PagedListAdapter 通知 RecyclerView 更新数据。

  1. 准备数据
  2. 引入Paging Library组件
  3. 自定义DataSource
  4. 配置分页参数
  5. 加载显示数据
  6. 测试效果
  7. Paging Library源码解析

准备数据

这里使用干货集中营的开源 Api 来进行测试,具体如下:

public interface CommonAPI {
    // 这里使用干货集中营开源API:http://gank.io/api/search/query/listview/category/Android/count/10/page/1
    @GET("api/search/query/listview/category/Android/count/8/page/{page}")
    Call<List<DataBean.ResultsBean>> getArticleList1(@Path("page") int page);
}

引入Paging Library组件

引入 Paging Library 库如下:

def paging_version = "2.1.0"
// androidx
implementation "androidx.paging:paging-runtime:$paging_version"
// 老版本 (page library 2.0.0-rc01)
implementation "android.arch.paging:runtime:$paging_version"

这里使用的时 androidx 最新版本。

自定义DataSource

自定义 DataSource 加载数据,这里加载的网络数据,使用 PageKeyedDataSource 更合适,继承 PageKeyedDataSource 自定义 DataSource 如下:

// 自定义DataSource
public class MDataSource extends PageKeyedDataSource<String, DataBean.ResultsBean> {
    private static final String TAG = MDataSource.class.getSimpleName();

    private int mPage = 1;

    public MDataSource() {
    }

    // 初始化
    @Override
    public void loadInitial(@NonNull LoadInitialParams<String> params,
                            @NonNull final LoadInitialCallback<String, DataBean.ResultsBean> callback) {
        Log.i(TAG, "--loadInitial-->");
        CommonAPI api = RetrofitApi.getInstance().mRetrofit.create(CommonAPI.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) {
                Log.i(TAG, "--onResponse-->" + response.toString());
                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.i(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) {
        Log.i(TAG, "--loadAfter-->" + params.key);
        mPage++;
        CommonAPI api = RetrofitApi.getInstance().mRetrofit.create(CommonAPI.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) {
                Log.i(TAG, "--onResponse-->" + response.toString());
                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());
            }

        });
    }
}

很简单没有什么多余的东西,细节可以参考后文中的源码解析,创建一个工厂方便数据变化是创建新的 DataSource 如下:

// MDataSource创建工厂
public class MDataSourceFactory extends DataSource.Factory<String, DataBean.ResultsBean> {
    public MDataSourceFactory() {
    }

    @NonNull
    @Override
    public DataSource<String, DataBean.ResultsBean> create() {
        MDataSource mDataSource = new MDataSource();
        return mDataSource;
    }
}

配置分页参数

在 ViewModel 中创建 PagedList.Config 并进行分页参数配置,创建 DataSource 工厂对象,最终生成支持分页的 LiveData 数据,具体参考如下:

// ViewModel
public class MViewModel 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 MDataSourceFactory();

    // LiveData
    private LiveData<PagedList<DataBean.ResultsBean>> mPagedList = new LivePagedListBuilder<>(factory, config)
            .build();

    public LiveData<PagedList<DataBean.ResultsBean>> getPagedList() {
        return mPagedList;
    }
}

加载显示数据

这里使用 LiveData 监听加载的数据,然后使用 sumbitList 将数据提交给 PagedListAdapter,会在后台线程中对比新旧数据的差异,最后更新 RecyclerView ,具体如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView mRecyclerView;
    private ArticleAdapter mAdapter;
    private MViewModel mViewModel;

    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() == newItem.getGanhuo_id();
        }

        @Override
        public boolean areContentsTheSame(@NonNull DataBean.ResultsBean oldItem, @NonNull DataBean.ResultsBean newItem) {
            return oldItem.equals(newItem);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = findViewById(R.id.rvData);
        mAdapter = new ArticleAdapter(itemCallback);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter);
        ViewModelProvider mViewModelProvider = new ViewModelProvider(this,
                new ViewModelProvider.AndroidViewModelFactory(getApplication()));
        mViewModel = mViewModelProvider.get(MViewModel.class);
    }

    public void getData(View view) {
        mViewModel.getPagedList().observe(this, new Observer<PagedList<DataBean.ResultsBean>>() {
            @Override
            public void onChanged(PagedList<DataBean.ResultsBean> dataBeans) {
                Log.i(TAG, "--onChanged-->");
                mAdapter.submitList(dataBeans);
            }
        });
    }
}

测试效果

可以关注公众号:躬行之 交流学习。