Android Recyclerview GridLayoutManager column spac

2019-01-01 16:27发布

How do you set the column spacing with a RecyclerView using a GridLayoutManager? Setting the margin/padding inside my layout has no effect.

23条回答
低头抚发
2楼-- · 2019-01-01 17:18
class VerticalGridSpacingDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {

  override fun getItemOffsets(
    outRect: Rect,
    view: View,
    parent: RecyclerView,
    state: State
  ) {
    val layoutManager = parent.layoutManager as? GridLayoutManager
    if (layoutManager == null || layoutManager.orientation != VERTICAL) {
      return super.getItemOffsets(outRect, view, parent, state)
    }

    val spanCount = layoutManager.spanCount
    val position = parent.getChildAdapterPosition(view)
    val column = position % spanCount
    with(outRect) {
      left = if (column == 0) 0 else spacing / 2
      right = if (column == spanCount.dec()) 0 else spacing / 2
      top = if (position < spanCount) 0 else spacing
    }
  }
}
查看更多
像晚风撩人
3楼-- · 2019-01-01 17:19

If you have a toggle switch that toggles between list to grid, don't forget to call recyclerView.removeItemDecoration() before setting any new Item decoration. If not then the new calculations for the spacing would be incorrect.


Something like this.

        recyclerView.removeItemDecoration(gridItemDecorator)
        recyclerView.removeItemDecoration(listItemDecorator)
        if (showAsList){
            recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
            recyclerView.addItemDecoration(listItemDecorator)
        }
        else{
            recyclerView.layoutManager = GridLayoutManager(this, spanCount)
            recyclerView.addItemDecoration(gridItemDecorator)
        }
查看更多
无与为乐者.
4楼-- · 2019-01-01 17:24

If you want to FIXED the size of your RecyclerView item in all devices. You can do like this

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

    private int mSpanCount;
    private float mItemSize;

    public GridSpacingItemDecoration(int spanCount, int itemSize) {
        this.mSpanCount = spanCount;
        mItemSize = itemSize;
    }

    @Override
    public void getItemOffsets(final Rect outRect, final View view, RecyclerView parent,
            RecyclerView.State state) {
        final int position = parent.getChildLayoutPosition(view);
        final int column = position % mSpanCount;
        final int parentWidth = parent.getWidth();
        int spacing = (int) (parentWidth - (mItemSize * mSpanCount)) / (mSpanCount + 1);
        outRect.left = spacing - column * spacing / mSpanCount;
        outRect.right = (column + 1) * spacing / mSpanCount;

        if (position < mSpanCount) {
            outRect.top = spacing;
        }
        outRect.bottom = spacing;
    }
}

recyclerview_item.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/recycler_view_item_width" 
    ...
    >
    ...
</LinearLayout>

dimens.xml

 <dimen name="recycler_view_item_width">60dp</dimen>

Activity

int numberOfColumns = 3;
mRecyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns));
mRecyclerView.setAdapter(...);
mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(3,
        getResources().getDimensionPixelSize(R.dimen.recycler_view_item_width)));

enter image description here enter image description here

查看更多
其实,你不懂
5楼-- · 2019-01-01 17:26

The selected answer is almost perfect, but depending on the space, items width can be not equal. (In my case it was critical). So i've ended up with this code which increases space a little bit, so items are all the same width.

class GridSpacingItemDecoration(private val columnCount: Int, @Px preferredSpace: Int, private val includeEdge: Boolean): RecyclerView.ItemDecoration() {

/**
 * In this algorithm space should divide by 3 without remnant or width of items can have a difference
 * and we want them to be exactly the same
 */
private val space = if (preferredSpace % 3 == 0) preferredSpace else (preferredSpace + (3 - preferredSpace % 3))

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State?) {
    val position = parent.getChildAdapterPosition(view)

    if (includeEdge) {

        when {
            position % columnCount == 0 -> {
                outRect.left = space
                outRect.right = space / 3
            }
            position % columnCount == columnCount - 1 -> {
                outRect.right = space
                outRect.left = space / 3
            }
            else -> {
                outRect.left = space * 2 / 3
                outRect.right = space * 2 / 3
            }
        }

        if (position < columnCount) {
            outRect.top = space
        }

        outRect.bottom = space

    } else {

        when {
            position % columnCount == 0 -> outRect.right = space * 2 / 3
            position % columnCount == columnCount - 1 -> outRect.left = space * 2 / 3
            else -> {
                outRect.left = space / 3
                outRect.right = space / 3
            }
        }

        if (position >= columnCount) {
            outRect.top = space
        }
    }
}

}

查看更多
素衣白纱
6楼-- · 2019-01-01 17:28

A Kotlin version I made based on the great answer by edwardaa

class RecyclerItemDecoration(private val spanCount: Int, private val spacing: Int) : RecyclerView.ItemDecoration() {

  override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {

    val spacing = Math.round(spacing * parent.context.resources.displayMetrics.density)
    val position = parent.getChildAdapterPosition(view)
    val column = position % spanCount

    outRect.left = spacing - column * spacing / spanCount
    outRect.right = (column + 1) * spacing / spanCount

    outRect.top = if (position < spanCount) spacing else 0
    outRect.bottom = spacing
  }

}
查看更多
登录 后发表回答