概要
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 しないのもダメ。サンプルコードを書けという感じだな^^;
ここ のもいいかも?未確認。