PagingSourceクラスの自作が必要(2)
getRefreshKey()メソッドの実装方法
PagingSourceクラスでは、もうひとつgetRefreshKey()メソッドをオーバーライドしておく必要があります。このメソッドは、リフレッシュなど、現在表示されているページデータが何らかの形で失われた場合に、どのリストデータを次に表示させればいいかの処理を記述します。
例えば、id(=キー)が200までのリストデータがページとして格納された状態で、そのうち120~130の10件が画面に表示されているとします。その状態で、何らかの理由でリストデータが失われたとします。その場合にスクロールが始まると、130以降のデータを取得しておく必要があります。
この開始キーを決めるのが、getRefreshKey()メソッドであり、例えば、リスト3のコードとなります。
override fun getRefreshKey(state: PagingState<Int, Phone>): Int? {
var returnVal: Int? = null; // (1)
val anchorPosition = state.anchorPosition // (2)
if(anchorPosition != null) { // (3)
val phone = state.closestItemToPosition(anchorPosition) // (4)
if(phone != null) { // (5)
val returnKey = phone.id.toInt() - state.config.pageSize / 2 // (6)
if(returnKey > 0) { // (7)
returnVal = returnKey
}
}
}
return returnVal
}
先述のように、getRefreshKey()メソッドの働きは、データが失われた際に再取得の開始キーを決めるメソッドです。従って、メソッド内の処理によって決定された開始キーを戻り値とします。そして、この戻り値の値が、そのままload()メソッドの引数paramsのkeyプロパティとして渡されます。もちろん、nullを戻り値とすることもでき、その場合は、nullがkeyプロパティとして渡されます。
リスト3の大きな流れとしては、リストデータが失われる前にどのデータが画面に表示されていたかを取得し、そこからid(=キー)の値を割り出します。割り出したidから1ページの表示件数の半分だけ戻ったid値を戻り値とする、といったコードです。
先の例で説明するならば、まずidが123あたりのデータを見ていたとし、そこから25(50件/ページの半分)戻った値、つまり、98を戻り値とします。結果、load()メソッドでは、この98という値をstartKeyとして、データを再取得することになり、画面がスクロールされてもスムーズに表示されるようになります。
ただし、随所にデータが取得できないことが考えられ、そのためのnull処理コードが必要となります。そして、データが取得できない場合は、そもそもgetRefreshKey()の戻り値もnullとし、結果、load()メソッドでは初期リストデータを用意するようにします。
これを踏まえ、(1)で戻り値変数であるreturnValを初期値nullとして用意しています。次に、引数であるPagingStateオブジェクトのプロパティanchorPositionを利用して、画面に表示されていたポジション値を取得します。それが、リスト3の(2)です。この値は、あくまでリストのポジションであって、id(=キー)の値ではありません。
そこで、ポジションに基づくアイテムオブジェクト(この場合はPhoneオブジェクト)を取得するために、同じくPagingStateのclosestItemToPosition()メソッドを利用します。それが、リスト3の(4)です。ただし、anchorPositionがnullの可能性を考慮して(3)のコードが必要です。
同様に、取得したアイテムオブジェクト(=Phoneオブジェクト)もnullの可能性があるので、そのチェックを(5)で行った上で、ようやくidの値を取得します。
そのidの値から1ページの表示件数の半分を引き算しているのが(6)です。ただし、この値は、場合によっては0以下になることもあり得ます。そのためのチェックを行っているのが(7)であり、ここまでの条件をクリアして、ようやく戻り値のreturnValとできます。これ以外は、全てnullがリターンされるようになります。
自作PagingSourceクラスの使い方はRoomと同じ
このようにして自作したPagingSourceクラスを利用する方法は、RoomのDAOオブジェクトから取得したPagingSourceオブジェクトと同じです。リスト4のように、Pagerインスタンスを生成する際の第2引数としてPagingSourceオブジェクトをリターンするラムダ式内で、自作のPagingSourceインスタンスを生成するだけです。
val phoneListPager = Pager(pagingConfig) {PhonePagingSource()}
