RecyclerView GridLayoutManager: スパンの数を自動検出するには? 質問する

RecyclerView GridLayoutManager: スパンの数を自動検出するには? 質問する

新しい GridLayoutManager の使用:参考:

明示的なスパン数が必要なので、問題は、行ごとにいくつの「スパン」が収まるかをどのように知るか、ということになります。結局のところ、これはグリッドです。測定された幅に基づいて、RecyclerView が収まるだけのスパンが必要です。

古い を使用するとGridView、「columnWidth」プロパティを設定するだけで、適合する列の数が自動的に検出されます。基本的に、これが RecyclerView で再現したい内容です。

  • OnLayoutChangeListenerを追加するRecyclerView
  • このコールバックでは、単一の「グリッドアイテム」を膨らませて測定します
  • spanCount = recyclerViewWidth / singleItemWidth;

これはかなり一般的な動作のようですが、私が気づいていないもっと簡単な方法があるのでしょうか?

ベストアンサー1

個人的には、このために RecyclerView をサブクラス化するのは好きではありません。なぜなら、スパン数を検出するのは GridLayoutManager の役割であるように思われるからです。そこで、RecyclerView と GridLayoutManager の Android ソース コードをいくつか調べた後、この目的を果たす GridLayoutManager を拡張した独自のクラスを作成しました。

public class GridAutofitLayoutManager extends GridLayoutManager
{
    private int columnWidth;
    private boolean isColumnWidthChanged = true;
    private int lastWidth;
    private int lastHeight;

    public GridAutofitLayoutManager(@NonNull final Context context, final int columnWidth) {
        /* Initially set spanCount to 1, will be changed automatically later. */
        super(context, 1);
        setColumnWidth(checkedColumnWidth(context, columnWidth));
    }

    public GridAutofitLayoutManager(
        @NonNull final Context context,
        final int columnWidth,
        final int orientation,
        final boolean reverseLayout) {

        /* Initially set spanCount to 1, will be changed automatically later. */
        super(context, 1, orientation, reverseLayout);
        setColumnWidth(checkedColumnWidth(context, columnWidth));
    }

    private int checkedColumnWidth(@NonNull final Context context, final int columnWidth) {
        if (columnWidth <= 0) {
            /* Set default columnWidth value (48dp here). It is better to move this constant
            to static constant on top, but we need context to convert it to dp, so can't really
            do so. */
            columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
                    context.getResources().getDisplayMetrics());
        }
        return columnWidth;
    }

    public void setColumnWidth(final int newColumnWidth) {
        if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
            columnWidth = newColumnWidth;
            isColumnWidthChanged = true;
        }
    }

    @Override
    public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
        final int width = getWidth();
        final int height = getHeight();
        if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
            final int totalSpace;
            if (getOrientation() == VERTICAL) {
                totalSpace = width - getPaddingRight() - getPaddingLeft();
            } else {
                totalSpace = height - getPaddingTop() - getPaddingBottom();
            }
            final int spanCount = Math.max(1, totalSpace / columnWidth);
            setSpanCount(spanCount);
            isColumnWidthChanged = false;
        }
        lastWidth = width;
        lastHeight = height;
        super.onLayoutChildren(recycler, state);
    }
}

なぜ onLayoutChildren でスパン数を設定することにしたのかは、実は覚えていません。このクラスはしばらく前に書いたものです。しかし、ポイントは、ビューを測定した後にこれを行う必要があるということです。そうすれば、高さと幅を取得できます。

編集1:スパンカウントを誤って設定するコードエラーを修正しました。ユーザーに感謝します@エリーズ・アブダ報告と提案解決

編集2:いくつかの小さなリファクタリングと、手動での方向変更処理に関するエッジケースの修正。ユーザーに感謝します。た ...報告と提案解決

おすすめ記事