如何使用GridLayoutManager与RecyclerView设置列间距? 在我的布局中设置空白/填充没有效果。


当前回答

选择的答案几乎是完美的,但根据空间的不同,项目的宽度可能不相等。(对我来说,这很关键)。所以我最终用这个代码增加了一点空间,所以项目都是相同的宽度。

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

}

其他回答

为了使https://stackoverflow.com/a/29905000/1649371(上面)解决方案工作,我必须修改以下方法(以及所有后续调用)

@SuppressWarnings("all")
protected int getItemSpanSize(RecyclerView parent, View view, int childIndex) {

    RecyclerView.LayoutManager mgr = parent.getLayoutManager();
    if (mgr instanceof GridLayoutManager) {
        return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex);
    } else if (mgr instanceof StaggeredGridLayoutManager) {
        return ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).isFullSpan() ? spanCount : 1;
    } else if (mgr instanceof LinearLayoutManager) {
        return 1;
    }

    return -1;
}

@SuppressWarnings("all")
protected int getItemSpanIndex(RecyclerView parent, View view, int childIndex) {

    RecyclerView.LayoutManager mgr = parent.getLayoutManager();
    if (mgr instanceof GridLayoutManager) {
        return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount);
    } else if (mgr instanceof StaggeredGridLayoutManager) {
        return ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
    } else if (mgr instanceof LinearLayoutManager) {
        return 0;
    }

    return -1;
}

我最后用GridLayoutManager和HeaderView为我的RecyclerView做了这样的事情。

在下面的代码中,我在每个项目之间设置了4dp空间(每个项目周围都是2dp,整个RecyclerView周围是2dp填充)。

在layout.xml:

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycleview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="2dp" />

在你的片段/活动中:

GridLayoutManager manager = new GridLayoutManager(getContext(), 3);
recyclerView.setLayoutManager(manager);
int spacingInPixels = Utils.dpToPx(2);
recyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels));

创建SpaceItemDecoration.java:

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {

    private int mSpacing;

    public SpacesItemDecoration(int spacing) {
        mSpacing = spacing;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView recyclerView, RecyclerView.State state) {
        outRect.left = mSpacing;
        outRect.top = mSpacing;
        outRect.right = mSpacing;
        outRect.bottom = mSpacing;
    }
}

在Utils.java:

public static int dpToPx(final float dp) {
    return Math.round(dp * (Resources.getSystem().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

对于这个问题,有一个非常简单而灵活的解决方案,它只使用适用于每个LayoutManager的XML。

假设你想要一个相等的X间距(例如8dp)。

在另一个布局中包装CardView项 给外层布局填充X/2 (4dp) 使外层布局背景透明

...

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@android:color/transparent"
    android:padding="4dip">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.CardView>

</LinearLayout>

给你的RecyclerView填充X/2 (4dp)

...

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="4dp" />

就是这样。你有完美的X (8dp)间距。

确保你在gradle模块中实现了这个:

implementation 'com.github.grzegorzojdana:SpacingItemDecoration:1.1.0'

创建这个简单的函数:

public static int dpToPx(Context c, int dp) {
    Resources r = c.getResources();
    return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
}

最后实现如下所示的空格:

photosRecycler.addItemDecoration(new SpacingItemDecoration(2,  dpToPx(this, 4), true));

选择的答案几乎是完美的,但根据空间的不同,项目的宽度可能不相等。(对我来说,这很关键)。所以我最终用这个代码增加了一点空间,所以项目都是相同的宽度。

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

}