Changing number of columns in RecyclerView gridlay

2020-02-03 07:30发布

问题:

I am trying to change the number of columns that appear in the recycler view (grid layout) based on the display size. However I couldn't figure out a proper way of achieving it. At the moment I am using treeViewObserver to change the number of columns based the change in screen size (during orientation). So if the app opens in portrait mode, number of columns (on the phone) it decides to be one, which look good but this method doesn't work when the app directly opens in landscape mode where a single streched out card in the grid is displayed on the screen.

Here recList is RecyclerView & glm is GridLayoutManager used in RecyclerView

    viewWidth = recList.getMeasuredWidth();

    cardViewWidthZZ = recList.getChildAt(0).getMeasuredWidth();

    if (oldWidth == 0) {
        oldWidth = cardViewWidthZZ;
    }

    if (oldWidth <= 0)
        return;

    int newSpanCount = (int) Math.floor(viewWidth / (oldWidth / 1.3f));
    if (newSpanCount <= 0)
        newSpanCount = 1;
    glm.setSpanCount(newSpanCount);
    glm.requestLayout();

Best Regards

回答1:

public class VarColumnGridLayoutManager extends GridLayoutManager {

private int minItemWidth;

public VarColumnGridLayoutManager(Context context, int minItemWidth) {
    super(context, 1);
    this.minItemWidth = minItemWidth;
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler,
        RecyclerView.State state) {
    updateSpanCount();
    super.onLayoutChildren(recycler, state);
}

private void updateSpanCount() {
    int spanCount = getWidth() / minItemWidth;
    if (spanCount < 1) {
        spanCount = 1;
    }
    this.setSpanCount(spanCount);
}}


回答2:

If you provide a fixed column width, you can extend RecyclerView and set the span count in onMeasure accordingly:

public AutofitRecyclerView(Context context, AttributeSet attrs) {
  super(context, attrs);

  if (attrs != null) {
    // Read android:columnWidth from xml
    int[] attrsArray = {
        android.R.attr.columnWidth
    };
    TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
    columnWidth = array.getDimensionPixelSize(0, -1);
    array.recycle();
  }

  manager = new GridLayoutManager(getContext(), 1);
  setLayoutManager(manager);
}

protected void onMeasure(int widthSpec, int heightSpec) {
  super.onMeasure(widthSpec, heightSpec);
  if (columnWidth > 0) {
    int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
    manager.setSpanCount(spanCount);
  }
}

See my blog post for more info: http://blog.sqisland.com/2014/12/recyclerview-autofit-grid.html



回答3:

Here's my solution.

public class ResponsiveGridLayoutManager extends GridLayoutManager {
    boolean hasSetupColumns;
    int columnWidthPx;

    public ResponsiveGridLayoutManager(Context context, int orientation,
        boolean reverseLayout, int columnWidthUnit, float columnWidth) {

        super(context, 1, orientation, reverseLayout);

        Resources r;
        if (context == null) {
            r = Resources.getSystem();
        } else {
            r = context.getResources();
        }

        float colWidthPx = TypedValue.applyDimension(columnWidthUnit,
            columnWidth, r.getDisplayMetrics());

        this.columnWidthPx = Math.round(colWidthPx);
    }

    public ResponsiveGridLayoutManager(Context context, AttributeSet attrs,
        int defStyleAttr, int defStyleRes) {

        super(context, attrs, defStyleAttr, defStyleRes);
        int[] requestedValues = {
            android.R.attr.columnWidth,
        };

        TypedArray a = context.obtainStyledAttributes(attrs, requestedValues);
        this.columnWidthPx = a.getDimensionPixelSize(0, -1);
        a.recycle();
    }

    @Override
    public int getWidth() {
        int width = super.getWidth();
        if (!hasSetupColumns && width > 0) {
            this.setSpanCount((int)Math.floor(width / this.columnWidthPx));
        }

        return width;
    }
}

You can use it with either XML:

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    app:layoutManager="com.caff.localflix.ResponsiveGridLayoutManager"
    android:columnWidth="148dp" />

Or Java:

ResponsiveGridLayoutManager layoutManager = new ResponsiveGridLayoutManager(
    this,
    GridLayoutManager.VERTICAL,
    false,
    TypedValue.COMPLEX_UNIT_DIP,
    148f
);


回答4:

You could create a screen dependent resource file and load it. A booleans.xml in values-w820p folder for example. Or possibly just create a layout for large screens and be done with it.