概要
-
FragmentStatePagerAdapter のバグで悩んだのでメモ
-
ここ の #52 に書かれているように 2018/7/31 時点でもバグが残ったままみたい。#11 のを使わせてもらったら想定通りの動きになった。
-
FragmentPagerAdapter は基本的に全 Fragment を保存するらしいので、あまり調べてない
-
FragmentStatePagerAdapter は基本的には見てるページと左右の3ページのみ保存するはず
-
ViewPager#setOffscreenPageLimit()でページ数を増やした場合は、そのページ分保存してくれる
詳細
-
問題は
FragmentStatePagerAdapter#getItemPosition()で正しい position を返しても保存されるべき Fragment が detach されてしまうこと- [A, B] の状態で C を真ん中に入れようとすると B の Fragment の instance の position の問い合わせが来るから、2 と返すと(0 origin なので 2) 、なぜか detach されてしまう。ので、[A, C, B] のつもりが [A, C, 空] となってしまう。
-
ぐぐると、常に
POSITION_NONEを返せばこのバグを回避できることがわかる。こことか。-
でも、これだと、なくなったことを Adapter に伝えるので毎回 detach されてしまう。その後、表示ページと左右の3ページ分の
getItem()が来て Fragment を生成するはめになる。 -
本来は、左右合わせて3ページ分は Adapter 内で保存してもらいたい。(または
setOffscreenPageLimit()で設定したページ分は保存してもらいたい。が、POSITION_NONEを返している場合は、これで多めに設定すると逆に detach → attach が増えて重くなる。。)
-
解決方法
-
ここ の #11 の
SortableFragmentStatePagerAdapterを使わせてもらえば問題なさそう-
ただし、
getItemId()で unique な ID を正しく返して、getItemPosition()で正しい position かPOSITION_NONEを返す必要あり。
position が変わらない場合はPOSITION_UNCHANGEDを返すべきだとは思うけど、position を返しておいても問題はなさそうに見える。 -
以下で問題なさそう
-
SortableFragmentStatePagerAdapterのサブクラスで、各ページの Fragment 情報を保持するリストを用意 -
ページを追加するときは、Fragment 情報と合わせて unique な ID も一緒にリストに追加する
-
getCount()ではそのリストのサイズを返すようにしておく -
notifyDataSetChanged()すると、リストのサイズが変わったときはgetItem()が来るので、そこでリスト内の Fragment 情報を使って Fragment 生成して、unique な ID をsetArguments()で渡してから Fragment の instance を返す -
直後(直前か?)に、既存ページの場所問い合わせで
getItemPosition(Fragmentのinstance)が来るので、Fragment 内に渡しておいた unique な ID を取得して、上記リスト内の ID と比較して position を求めて返す -
getItemId()の使われ方はあまり調べてないが、notifyDataSetChanged()したときにページの順番が変わってないかをチェックするのに使われるはず。とりあえず、上記と同じ unique な ID を返しておけば問題なさそう。引数の position をそのまま返すだけじゃダメ。override しないのもダメ。 -
サンプルコードを書けという感じだな^^;
-
-
-
ここ のもいいかも?未確認。