Posted at

ListViewで追加取得したときに現在位置のまま表示更新する

More than 5 years have passed since last update.

少し調べれば、やり方は簡単なのだけど、気をつけるべきポイントに触れてる情報が少なかったので、まとめました。


サンプルコードの条件


  • ListFragmentのサブクラスで実装

  • データ取得は非同期処理


やりたいこと


  1. ListViewで一番下までスクロールしたら、追加データを取得する

  2. ArrayAdapterにデータを追加し、更新する

  3. 更新前にリストの一番下だったところから、そのまま追加されたリストを見れるようにする

※いわゆるTwitterクライアンとかで、よくある動きです。


一番下までスクロールしたときのイベント登録

ListViewでは、スクロールしているときのイベントをハンドルするためのインターフェース(AdsListView.OnScrollListener)が、用意されてます。


SubListFragment.java

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();