RecyclerView GridLayoutManager: how to auto-detect

2019-01-07 02:35发布

Using the new GridLayoutManager: https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html

It takes an explicit span count, so the problem now becomes: how do you know how many "spans" fit per row? This is a grid, after all. There should be as many spans as the RecyclerView can fit, based on measured width.

Using the old GridView, you would just set the "columnWidth" property and it would automatically detect how many columns fit. This is basically what I want to replicate for the RecyclerView:

  • add OnLayoutChangeListener on the RecyclerView
  • in this callback, inflate a single 'grid item' and measure it
  • spanCount = recyclerViewWidth / singleItemWidth;

This seems like pretty common behavior, so is there a simpler way that I'm not seeing?

12条回答
欢心
2楼-- · 2019-01-07 03:14

The upvoted solution is fine, but handles the incoming values as pixels, which can trip you up if you're hardcoding values for testing and assuming dp. Easiest way is probably to put the column width in a dimension and read it when configuring the GridAutofitLayoutManager, which will automatically convert dp to correct pixel value:

new GridAutofitLayoutManager(getActivity(), (int)getActivity().getResources().getDimension(R.dimen.card_width))
查看更多
何必那么认真
3楼-- · 2019-01-07 03:15

I'm posting this just in case someone gets weird column width as in my case.

I'm not able to comment on @s-marks's answer due to my low reputation. I applied his solution solution but I got some weird column width, so I modified checkedColumnWidth function as follows:

private int checkedColumnWidth(Context context, 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());
    }

    else
    {
        columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, columnWidth,
                context.getResources().getDisplayMetrics());
    }
    return columnWidth;
}

By converting the given column width into DP fixed the issue.

查看更多
啃猪蹄的小仙女
4楼-- · 2019-01-07 03:17

I extended the RecyclerView and overrode the onMeasure method.

I set an item width(member variable) as early as I can,with a default of 1. This also updates on configuration changed. This will now have as many rows as can fit in portrait,landscape,phone/tablet etc.

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
    super.onMeasure(widthSpec, heightSpec);
    int width = MeasureSpec.getSize(widthSpec);
    if(width != 0){
        int spans = width / mItemWidth;
        if(spans > 0){
            mLayoutManager.setSpanCount(spans);
        }
    }
}
查看更多
冷血范
5楼-- · 2019-01-07 03:18

Set spanCount to a large number (which is the max number of column) and set a custom SpanSizeLookup to the GridLayoutManager.

mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int i) {
        return SPAN_COUNT / (int) (mRecyclerView.getMeasuredWidth()/ CELL_SIZE_IN_PX);
    }
});

It's a bit ugly, but it work.

I think a manager like AutoSpanGridLayoutManager would be the best solution, but i didn't find anything like that.

EDIT : There is a bug, on some device it add blank space to the right

查看更多
霸刀☆藐视天下
6楼-- · 2019-01-07 03:22

To accommodate orientation change on s-marks's answer, I added a check on width change (width from getWidth(), not column width).

private boolean mWidthChanged = true;
private int mWidth;


@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
{
    int width = getWidth();
    int height = getHeight();

    if (width != mWidth) {
        mWidthChanged = true;
        mWidth = width;
    }

    if (mColumnWidthChanged && mColumnWidth > 0 && width > 0 && height > 0
            || mWidthChanged)
    {
        int totalSpace;
        if (getOrientation() == VERTICAL)
        {
            totalSpace = width - getPaddingRight() - getPaddingLeft();
        }
        else
        {
            totalSpace = height - getPaddingTop() - getPaddingBottom();
        }
        int spanCount = Math.max(1, totalSpace / mColumnWidth);
        setSpanCount(spanCount);
        mColumnWidthChanged = false;
        mWidthChanged = false;
    }
    super.onLayoutChildren(recycler, state);
}
查看更多
我想做一个坏孩纸
7楼-- · 2019-01-07 03:23

I conclusion above answers here

查看更多
登录 后发表回答