ListViewで表示位置を保存/復帰するには ListView#getFirstVisiblePosition/ListView#getChildAt(0).getTop()と ListView#SetSelectionFromTop()を使うというのは定石だと思う。
でも、GridViewで同じ事をするには?
具体的には端末の回転時に GridViewの表示位置を保ちたい。
何も考えずに GridView#getFirstVisiblePosition/GridView#SetSelectionを使うと大抵は悲しい結果になる。
ポートレイトとランドスケープで GridViewに設定するカラム数*1 が同じというのは考えづらいから。
例えば、ポートレイト時の桁数が 2、ランドスケープ時の桁数が 3だとして、ポートレイト時にGridView#getFirstVisiblePositionが 4を返してランドスケープになって GridView#SetSelectionに 4を設定するとその時は確かにうまくいっているように見える。
この状態で GridView#getFirstVisiblePositionは 3を返す事になる。*2
すると、ポートレイトにしたときに GridView#SetSelectionに 3を指定することによって、最初のポートレイトの時より一行上から表示されることになる。
更にランドスケープにすると GridView#getFirstVisiblePositionは 2を返して…端末の向きを変えるだけでどんどん表示位置が変わってしまうわけだ。
理屈はわかるのだけど、これはあまりうれしくない。
けど、どういう動作が理想なのかと考えるとなかなか難しく、今回は「端末を回転させても最後にユーザーがスクロールさせた位置を可能な限り再現する」という方向で考えることにした。
要点は以下の三つ。
- OnScrollListener#onScrollStateChangedにて scrollState == SCROLL_STATE_IDLEが成立する時に GridView#getFirstVisiblePosition/GridView#getChildAt(0).getTop()を呼び出して値を保存しておく。(pos,top)
- onSaveInstanceState/onActivityCreatedでは先の二つの値を保存/復帰する。
- アダプタを設定したら GridView#setSelection(pos)/GridView#smoothScrollBy(-top,0)を呼び出す。
これによって、ユーザーが最後にスクロールさせた時に左上に表示されていた項目が表示の 1行目に来るように調整されることになり、目的が達成される。
以上、ListViewと違って GridViewでの定石のような物が見あたらなかったので一例として記録を残してみた。