LayoutManager for RecyclerView Grid with different cell width

后端 未结 3 1624
半阙折子戏
半阙折子戏 2020-12-04 07:34

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

3条回答
  •  南方客
    南方客 (楼主)
    2020-12-04 08:29

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

    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:

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

提交回复
热议问题