APIから取得したデータを表示するだけの場合
この要件の際はPagingSourceを実装して、PagerのpagingSourceFactoryに指定するだけで実装可能です。
実装の完成系はサンプルコードのmasterブランチをご覧ください
PagingSourceの実装
PagingSourceで実装する必要があるメソッドは2つあります。
- getRefreshKeyメソッド: refreshの際に使用するkeyを返却するメソッド
- loadメソッド:LoadParamsからkeyを取得し、それをAPIのPageとして指定しデータを取得するメソッド
getRefreshKeyは、refreshの際に使うkeyを返却するメソッドです。まずstateのclosestPageToPosition関数で指定したpositionのデータ取得することができます。データが取得できた場合は、loadメソッドで返却しているLoadResult.Pageが取得できるので、そのprevKeyとnextKeyから現在のKeyを算出して返却しています。また、指定したpositionのデータが見つからなかった場合はnullを返却します。
loadメソッドは、LoadParamsからkeyを取得し、それをAPIのPageとして指定しデータを取得します。初期読み込みの場合などはkeyがnullで渡されるため、API側のpage指定の最小値でAPIの呼び出しを行います。
APIでデータを取得できた場合LoadResult.Pageとしてデータと1つ前のkey、次のkeyを指定して返却します。エラーならLoadResult.Errorを返却します。
class AnimePagingSource(
private val service: AnimeService,
) : PagingSource<Int, Anime>() {
// APIのpage指定の最小値
private val FIRST_INDEX = 0
// APIの1チャンクあたりの取得データ数
private val PAGE_SIZE = 30
override fun getRefreshKey(state: PagingState<Int, Anime>): Int? {
val position = state.anchorPosition ?: return null
val prevKey = state.closestPageToPosition(position)?.prevKey
val nextKey = state.closestPageToPosition(position)?.nextKey
return prevKey?.plus(1) ?: nextKey?.minus(1)
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Anime> {
val position = params.key ?: FIRST_INDEX
return try {
withContext(Dispatchers.IO) {
val result = service.getAnimes(
token = "取得したトークン", // APIの呼び出しに必要なため
page = position,
perPage = PAGE_SIZE,
)
val repositories = result.body()?.animeList
val nextKey = if (repositories.isNullOrEmpty()) null else position + 1
return@withContext LoadResult.Page(
data = repositories ?: listOf(),
prevKey = if (position >= FIRST_INDEX) position 1 else FIRST_INDEX,
nextKey = nextKey
)
}
} catch (e: Exception) {
return LoadResult.Error(e)
}
}
}
Repositoryの実装
APIから取得したデータを表示するだけの場合は、Pagerの引数に、共通部分の説明で実装したPagingConfigに加え、pagingSourceFactoryに作成したPagingSourceを指定します。
この際の注意点として、pagingSourceFactoryの引数が() -> PagingSource<Key, Value>となっているため、下記のコードで渡している形式ではなく、PagingSourceのインスタンスを渡すだけといった場合ではエラーになるので気を付けてください。
class AnimeRepository @Inject constructor(
private val api: AnimeApi,
private val service: AnimeService,
) {
fun getAnimes(): Pager<Int, Anime> =
Pager(
config = PagingConfig(
pageSize = 20,
prefetchDistance = 60,
enablePlaceholders = false,
),
pagingSourceFactory = {
AnimePagingSource(
service = service
)
},
)
}
Repository以降のFragmentやAdapterは共通の実装の部分と同じですので、ここまで実装するとAPIから取得したデータを表示するだけの場合の実装ができます。
ここまで実装したところで、実行して表示を確認してみましょう。
上記のように、APIから取得したデータをRecyclerViewでリスト表示できています。そしてスクロールすることでページング処理を行えていることが確認できます。 都合上サンプルアプリと異なり上記の画面キャプチャではサムネイルは非表示にしています。
