web-dev-qa-db-ja.com

セル幅が異なるRecyclerViewグリッドのLayoutManager

StaggeredGridLayoutManagerでは、セル幅をカスタマイズしたり、垂直方向の複数列(フルスパンを除く)をスパンしたりすることはできません。

enter image description here

上記のようにセルを整理するのに好ましいLayoutManagerは何ですか?

P.S。StaggeredGridLayoutManagerで高さではなくセル幅をカスタマイズする方法を知りたいだけです。この sample で実装されているように、高さをカスタマイズできることを知っています。

_public class VerticalStaggeredGridFragment extends RecyclerFragment {

    public static VerticalStaggeredGridFragment newInstance() {
        VerticalStaggeredGridFragment fragment = new VerticalStaggeredGridFragment();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    protected RecyclerView.LayoutManager getLayoutManager() {
        return new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    }

    @Override
    protected RecyclerView.ItemDecoration getItemDecoration() {
        return new InsetDecoration(getActivity());
    }

    @Override
    protected int getDefaultItemCount() {
        return 100;
    }

    @Override
    protected SimpleAdapter getAdapter() {
        return new SimpleStaggeredAdapter();
    }
}
_

アダプター

_public class SimpleStaggeredAdapter extends SimpleAdapter {
    @Override
    public void onBindViewHolder(VerticalItemHolder itemHolder, int position) {
        super.onBindViewHolder(itemHolder, position);

        final View itemView = itemHolder.itemView;
        if (position % 4 == 0) {
            int height = itemView.getContext().getResources()
                    .getDimensionPixelSize(R.dimen.card_staggered_height);
            itemView.setMinimumHeight(height);
        } else {
            itemView.setMinimumHeight(0);
        }
    }
}
_

itemView.setMinimumWidth(customWidth)はセル幅に影響しません。

簡単にするために、グリッドレイアウトを

enter image description here

StaggeredGridLayoutManagerまたは他のレイアウトマネージャーのセル1と比較してセル0の幅を増やす方法は?

23
random

StaggeredGridLayoutManager.LayoutParamsを使用してセルの幅と高さを変更できます。設計するものは単純なパターンに従います。すべてのセルにインデックスを割り当てると(デフォルトでStaggeredGridLayoutManager配置される順序で)これを持っています:

index % 4 == 3           --->  full span cell
index % 8 == 0, 5        --->  half span cell
index % 8 == 1, 2, 4, 6  ---> quarter span cell

最初にアダプターでいくつかの定数を宣言して、スパンタイプを定義します。

private static final int TYPE_FULL = 0;
private static final int TYPE_HALF = 1;
private static final int TYPE_QUARTER = 2;

次に、アダプターのgetItemViewTypeメソッドを次のようにオーバーライドします。

@Override
public int getItemViewType(int position) {
    final int modeEight = position % 8;
    switch (modeEight) {
        case 0:
        case 5:
            return TYPE_HALF;
        case 1:
        case 2:
        case 4:
        case 6:
            return TYPE_QUARTER;
    }
    return TYPE_FULL;
}

必要なことは、viewTypeholderを考慮してlayoutparamsを変更することだけです。

@Override
public MyHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
    final View itemView =
            LayoutInflater.from(mContext).inflate(R.layout.items, parent, false);
    itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            final int type = viewType;
            final ViewGroup.LayoutParams lp = itemView.getLayoutParams();
            if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
                StaggeredGridLayoutManager.LayoutParams sglp =
                        (StaggeredGridLayoutManager.LayoutParams) lp;
                switch (type) {
                    case TYPE_FULL:
                        sglp.setFullSpan(true);
                        break;
                    case TYPE_HALF:
                        sglp.setFullSpan(false);
                        sglp.width = itemView.getWidth() / 2;
                        break;
                    case TYPE_QUARTER:
                        sglp.setFullSpan(false);
                        sglp.width = itemView.getWidth() / 2;
                        sglp.height = itemView.getHeight() / 2;
                        break;
                }
                itemView.setLayoutParams(sglp);
                final StaggeredGridLayoutManager lm =
                        (StaggeredGridLayoutManager) ((RecyclerView) parent).getLayoutManager();
                lm.invalidateSpanAssignments();
            }
            itemView.getViewTreeObserver().removeOnPreDrawListener(this);
            return true;
        }
    });

    MyHolder holder = new MyHolder(itemView);
    return holder;
}

私のアダプターは、アイテム数(テストのみ)に対して常に50を返し、アイテムの単純なレイアウトファイルを使用しました。これには、LinearLayoutおよびTextViewが含まれ、ホルダーの位置を示します。 spanCountStaggeredGridLayoutManagerコンストラクター)の設計のため。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
    StaggeredGridLayoutManager lm =
            new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    rv.setLayoutManager(lm);
    rv.setAdapter(new MyAdapter(this));

}

enter image description here

[〜#〜] ps [〜#〜]:私のような怠け者にとっては、カスタムLayoutManagerを拡張するよりも簡単な方法かもしれません、しかし、これを達成するより良い方法があると確信しています。

Update:質問の更新された部分がよりシンプルになり、GridLayoutManagerを使用できます:

    GridLayoutManager glm = new GridLayoutManager(this, 3);
    glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            if (position % 3 == 2) {
                return 3;
            }
            switch (position % 4) {
                case 1:
                case 3:
                    return 1;
                case 0:
                case 2:
                    return 2;
                default:
                    //never gonna happen
                    return  -1 ;
            }
        }
    });
    rv.setLayoutManager(glm);

このようにして、デフォルトのスパンサイズに3を設定し、スパンの位置を考慮して、各アイテムが1、2、または3スパンを占有します。次のようなものです。

enter image description here

Item0.width == 2 X Item1.width
Item2.width == 3 X Item1.width
Item0.width == 2/3 X Item1.width
48

レイアウトを単純化せずにソリューションを探している他のすべての人は、 私の答えはこちら を参照するか、詳細を読むことができます。

Nick Butcher !に感謝します! SpannableGridLayoutManagerと呼ばれる彼のレイアウトマネージャーはこれを行うことができます。

  1. ダウンロードSpannableGridLayoutManager ここから

    何らかの理由で、この行を変更する必要がありました。

    while (availableSpace > 0 && lastVisiblePosition < lastItemPosition) {
    

    while (lastVisiblePosition < lastItemPosition) {
    

    マネージャーを機能させるために。

  2. SpannableGridLayoutMangerをご使用のRecyclerViewに設定します

あなたの場合:

SpannedGridLayoutManager manager = new SpannedGridLayoutManager(
            new SpannedGridLayoutManager.GridSpanLookup() {
                @Override
                public SpannedGridLayoutManager.SpanInfo getSpanInfo(int position) {
                    switch (position % 8) {
                        case 0:
                        case 5:
                            return new SpannedGridLayoutManager.SpanInfo(2, 2);

                        case 3:
                        case 7:
                            return new SpannedGridLayoutManager.SpanInfo(3, 2);

                        default:
                            return new SpannedGridLayoutManager.SpanInfo(1, 1);
                    }
                }
            },
            3, // number of columns
            1f // default size of item
    );

これにより、質問の最初の写真にある内容が正確にわかります。

enter image description here

7
kristyna