LayoutManager for RecyclerView Grid with different

2020-02-02 04:20发布

StaggeredGridLayoutManager doesn't seem to allow customising a cell width or span multiple columns (except full span) for vertical orientation.

enter image description here

What is a preferred LayoutManager for organising cells as shown above?

P.S. I just want to know how to customise cell width and not height with StaggeredGridLayoutManager. I know height can be customised as implemented in this 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();
    }
}

Adapter

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) doesn't affect cell width.

To simplify, I update my grid layout to

enter image description here

How to increase cell 0 width as compared to cell 1 in a StaggeredGridLayoutManager or any other layout manager?

2条回答
淡お忘
2楼-- · 2020-02-02 04:31

For everyone else, looking for a solution without simplifying your layout, you can have a look to my answer here, or you can read more.

Big thanks to Nick Butcher! His layout manager called SpannableGridLayoutManager is capable of doing this.

  1. Download SpannableGridLayoutManager from here

    For some reason I had to change this line:

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

    to

    while (lastVisiblePosition < lastItemPosition) {
    

    to got the manager working.

  2. Set SpannableGridLayoutManger to your RecyclerView

In your case:

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
    );

Which will give you exactly what is in the first picture of the question.

enter image description here

查看更多
劳资没心,怎么记你
3楼-- · 2020-02-02 04:39

You can use StaggeredGridLayoutManager.LayoutParams to change width and height of cells, What you expect to design follows a simple pattern, If you assign every cell an index (in the order that StaggeredGridLayoutManager arranges them by default) you will have this:

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

first declare some constants in adapter to define span types:

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

Then override getItemViewType method in your adapter like this:

@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;
}

All you need to do is change layoutparams considering viewType of the holder:

@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;
}

My adapter always returns 50 for items count(just a test) and I used a simple layout file for items, It contains a LinearLayout and a TextView to show the position of holder, Remember you should pass 2 for spanCount (int the StaggeredGridLayoutManager constructor) because of your design.

@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: For lazy people like me maybe it's a simpler way rather than extending my custom LayoutManager, But I'm sure there are better ways to achieve this.

Update: Updated part of your question is more simpler, You can use 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);

In this way you set 3 for default span size, Then considering the position of your span, Each item occupies 1, 2 or 3 spans, Something like this:

enter image description here

Item0.width == 2 X Item1.width
Item2.width == 3 X Item1.width
Item0.width == 2/3 X Item1.width
查看更多
登录 后发表回答