web-dev-qa-db-ja.com

アダプタで設定した後、RecyclerViewがアイテムの表示を終了したときのコールバックはありますか?

バックグラウンド

私はRecyclerViewの高速スクローラーを表示するライブラリを作成しました( here 、だれでもが望む場合)、そして私は高速スクロールを表示するタイミングと非表示にするタイミングを決定したい。

画面に表示されていない(または表示されていないアイテムが多数ある)アイテムがある場合、RecyclerViewがレイアウトプロセスを完了した後、高速スクロールを表示され、すべてのアイテムが既に表示されている場合は、表示する必要はありません。

問題

RecyclerViewのリスナー/コールバックが見つからないため、アイテムの表示が終了したときに通知されるため、アイテム数と比較して表示されるアイテムの数を確認できます。

RecyclerViewは、キーボードの表示と非表示を切り替えるときにサイズを変更することもあります。

私が試したこと

スクロールリスナは「常に」発生するため、おそらく役に立ちません。RecyclerViewのサイズが変更されたとき、またはアイテム数(またはデータ)が変更されたときのみチェックする必要があります。

this one のようにサイズの変更を通知するレイアウトでRecyclerViewをラップすることもできますが、RecyclerViewはおそらくまだ準備ができていないので機能するとは思わない表示されるアイテムの数を示します。

表示されているアイテムの数を確認する方法は、次のように使用できます。

    final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
    mRecyclerView.setLayoutManager(layoutManager);
    ...
    Log.d("AppLog", "visible items count:" + (layoutManager.findLastVisibleItemPosition() -layoutManager.findFirstVisibleItemPosition()+1));

質問

RecyclerViewがその子ビューの表示を終了したときに通知を受け取るには、現在表示されているものに基づいて高速スクロールを表示/非表示するように決定するにはどうすればよいですか?

40

LayoutManagerのコールバックを使用して、これを解決する方法を見つけました(ユーザーのおかげで pskink )。

final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public void onLayoutChildren(final Recycler recycler, final State state) {
                super.onLayoutChildren(recycler, state);
                //TODO if the items are filtered, considered hiding the fast scroller here
                final int firstVisibleItemPosition = findFirstVisibleItemPosition();
                if (firstVisibleItemPosition != 0) {
                    // this avoids trying to handle un-needed calls
                    if (firstVisibleItemPosition == -1)
                        //not initialized, or no items shown, so hide fast-scroller
                        mFastScroller.setVisibility(View.GONE);
                    return;
                }
                final int lastVisibleItemPosition = findLastVisibleItemPosition();
                int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
                //if all items are shown, hide the fast-scroller
                mFastScroller.setVisibility(mAdapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
            }
        };

ここで良いことは、それがうまく機能し、キーボードが表示/非表示であっても処理できることです。

悪い点は、面白くないケースで呼び出されることです(誤検知があることを意味します)が、スクロールイベントほど頻繁ではないので、私には十分です。


編集:後で追加されたコールバックがあり、複数回呼び出されることはありません。上に書いたものではなく、新しいコードを次に示します。

        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public void onLayoutCompleted(final State state) {
                super.onLayoutCompleted(state);
                final int firstVisibleItemPosition = findFirstVisibleItemPosition();
                final int lastVisibleItemPosition = findLastVisibleItemPosition();
                int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
                //if all items are shown, hide the fast-scroller
                fastScroller.setVisibility(adapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
            }
        });
26

これには「addOnGlobalLayoutListener」を使用しています。これが私の例です:

ロード後に必要なアクションを実行するためのインターフェースの定義:

public interface RecyclerViewReadyCallback {
  void onLayoutReady();
}

recyclerViewで、ロードの準備ができたらonLayoutReadyメソッドをトリガーします。

mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
  if (recyclerViewReadyCallback != null) {
    recyclerViewReadyCallback.onLayoutReady();
  }
  recyclerViewReadyCallback = null;
});

注:メソッドが複数回呼び出されないようにするには、nullに設定する必要があります。

0
Peter