この記事を読む前に、同シリーズの Android Jetpack コンポーネントに関する記事を以下に示します:
- Android Jetpack コンポーネントの Lifecycle 編
- Android Jetpack コンポーネントの LiveData 編
- Android Jetpack コンポーネントの ViewModel 編
- Android Jetpack コンポーネントの DataBinding 編
- Android Jetpack コンポーネントの BindingAdapter 編
- Android Jetpack コンポーネントの使用可能な観察可能データオブジェクト
この記事では Paging Library の使用について説明します。そのソースコードの解析は次の記事で紹介します。Paging Library コンポーネントは Android Jetpack の一部であり、Google が提供する公式のページネーションコンポーネントです。プロジェクトで Google が新たに提供する公式のアーキテクチャコンポーネント(LiveData、Lifecycle、ViewModel など)を使用している場合は、このページネーションコンポーネントをプロジェクトに導入することを検討できます。その利点は、データをシームレスに追加読み込みでき、ユーザー体験を向上させることができる点です。
Paging コンポーネントを使用してデータをページングして読み込むプロセスを簡単に説明します。DataSource はネットワークまたはデータベースからデータを読み込み、データを PagedList に格納します。submitList を使用してデータを PagedListAdapter に提出し、データが変更されると、バックグラウンドスレッドでデータの差分が計算され、最終的に PagedListAdapter が RecyclerView にデータの更新を通知します。
- データの準備
- Paging Library コンポーネントの導入
- カスタム DataSource の作成
- ページングパラメータの設定
- データの読み込みと表示
- 効果のテスト
- 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)//1ページあたりの読み込み数を設定
.setPrefetchDistance(2)//次のページデータを事前に読み込むための最後のデータ項目までの距離を設定
.setEnablePlaceholders(false)//UIプレースホルダーを有効にするかどうかを設定
.build();
// DataSource.Factory
private DataSource.Factory<String,DataBean.Results