Help us understand the problem. What is going on with this article?

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();
finc
健康寿命を伸ばすアプリFiNCの開発・運営を行うモバイルヘルステクノロジーベンチャー
https://finc.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away