1. ホーム
  2. android

[解決済み] RecyclerView GridLayoutManager: スパン数を自動検出する方法は?

2022-08-26 19:06:22

質問

新しいGridLayoutManagerを使用する。 https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html

これは明示的なスパン数を取るので、問題は、1 行に収まる "スパン" の数をどのように知るか、ということになります。これは結局のところ、グリッドです。測定された幅に基づいて、RecyclerViewが適合できるのと同じ数のスパンがあるはずです。

古い GridView を使用すると、ちょうど "columnWidth" プロパティを設定し、それは自動的にフィットするいくつかの列を検出するでしょう。これは基本的に、私がRecyclerViewのために再現したいものです。

  • の上にOnLayoutChangeListenerを追加します。 RecyclerView
  • このコールバックで、単一の 'グリッド アイテム' を生成し、それを測定します。
  • spanCount = recyclerViewWidth / singleItemWidth;

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

どのように解決するのですか?

個人的には、RecyclerViewのサブクラスを作成するのは好きではありません。そこで、RecyclerViewとGridLayoutManagerのためのいくつかのアンドロイドのソースコードを掘った後、私は仕事をする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でスパン数を設定することにしたのか、このクラスを書いたのは少し前なので、実は覚えていないのです。しかし、重要なのは、ビューが計測された後、高さと幅を取得するために、そうする必要があるということです。

EDIT 1: スパン数の設定ミスによるコードの誤りを修正しました。ありがとうございました。 Elyees Abouda の報告および提案に感謝します。 解決策 .

EDIT 2: いくつかの小さなリファクタリングと、手動方向変更処理に関するエッジケースを修正しました。ありがとうございました。 を追加しました。 の報告および提案に感謝します。 解決策 .