少し調べれば、やり方は簡単なのだけど、気をつけるべきポイントに触れてる情報が少なかったので、まとめました。
サンプルコードの条件
- ListFragmentのサブクラスで実装
- データ取得は非同期処理
やりたいこと
- ListViewで一番下までスクロールしたら、追加データを取得する
- ArrayAdapterにデータを追加し、更新する
- 更新前にリストの一番下だったところから、そのまま追加されたリストを見れるようにする
※いわゆるTwitterクライアンとかで、よくある動きです。
一番下までスクロールしたときのイベント登録
ListViewでは、スクロールしているときのイベントをハンドルするためのインターフェース(AdsListView.OnScrollListener)が、用意されてます。
public class SubListFragment extends ListFragment {
…
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Scrollイベント設定
ListView listView = getListView();
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
@Override
public void onScroll(AbsListView absListView,
int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (totalItemCount != 0 && totalItemCount == firstVisibleItem + visibleItemCount) {
// 最後尾までスクロールしたので、何かデータ取得する処理
...
}
}
});
}
onScroll()メソッドで渡されてくる数値は、以下の通りです。
- firstVisibleItem : 画面上での一番上のリストの番号
- visibleItemCount : 画面内に表示されているリストの個数
- totalItemCount : ListViewが持つリストの総数
なので、以下の判定文で画面に表示している(画面上に流れていったものも含めた)リストが、ListViewの持つ総数に等しいかチェック出来ます。
if (totalItemCount == firstVisibleItem + visibleItemCount) {}
表示している位置の取得とセット
例えば今表示してるリストの位置を取り出し、ListViewにセットするようなメソッドを作る場合は、以下のような感じになります。
private void restoreListPosition() {
ListView listView = getListView();
int position = listView.getFirstVisiblePosition();
int yOffset = listView.getChildAt(0).getTop();
listView.setSelectionFromTop(position, yOffset);
}
ListViewの位置を設定する正しいタイミング
setSelectionFromTop()メソッドは、適切なタイミングで呼んであげないと期待する動きになりません。
以下はデータ取得するときの、レスポンス処理あたりでの実装例です。
mSubAdapter.add(data);
...
mSubAdapter.notifyDataSetChanged();
restoreListPosition();
ここでmSubAdapter
は、ArrayAdapter<T>
をカスタマイズしたクラスのことです。
add()メソッドでデータを追加した後に、notifyDataSetChanged()メソッドでリストデータに変更があることをListViewに通知します。
その後で先程のsetSelectionFromTop()メソッドを呼んであげると、指定した位置でListViewの更新ができます。
ListViewの位置を設定する間違ったタイミング
注意しないといけないのは、setSelectionFromTop()メソッドを呼ぶ前に、setListAdapter()メソッドを呼ぶのはNGです。アプリがクラッシュします。
mSubAdapter.add(data);
...
setListAdapter(mSubAdapter);
...
mSubAdapter.notifyDataSetChanged();
restoreListPosition();