Как избежать двойного пробела между элементами при использовании RecyclerView с StaggeredGridLayoutManager?

Я использую RecyclerView со StaggeredGridLayoutManager, чтобы создать список из двух столбцов. Но как установить правое поле между левым столбцом и правым столбцом. Я использовал этот код, чтобы сделать правое поле сверху, но как решить двойное пространство между столбцами.

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private int space;

    public SpacesItemDecoration(int space) {
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.left = space;
        outRect.right = space;
        outRect.bottom = space;

        // Add top margin only for the first or second item to avoid double space between items
        // Add top margin only for the first or second item to avoid double space between items
        if((parent.getChildCount() > 0 && parent.getChildPosition(view) == 0)
            || (parent.getChildCount() > 1 && parent.getChildPosition(view) == 1))
        outRect.top = space;
}

И в действии:

recyclerView.addItemDecoration(new SpacesItemDecoration(20));

Я пытался использовать view.getX(), он всегда возвращает 0.

Может кто-нибудь помочь мне? Большое спасибо!


person outofmemory    schedule 07.03.2015    source источник


Ответы (8)


Следующий код будет обрабатывать StaggeredGridLayoutManager, GridLayoutManager и LinearLayoutManager.

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {

    private int halfSpace;

    public SpacesItemDecoration(int space) {
        this.halfSpace = space / 2;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

        if (parent.getPaddingLeft() != halfSpace) {
            parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
            parent.setClipToPadding(false);
        }

        outRect.top = halfSpace;
        outRect.bottom = halfSpace;
        outRect.left = halfSpace;
        outRect.right = halfSpace;
    }
}
person Mark Hetherington    schedule 24.08.2015

В вашем случае вы можете установить оба поля для RecyclerView. Но в моем случае изображение соответствует ширине экрана в RecyclerView, и я использую ItemDecoration для решения проблемы.

  class ViewItemDecoration extends RecyclerView.ItemDecoration {

    public ViewItemDecoration() {
    }

    @Override
    public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        int type = adapter.getItemViewType(position);
        switch(type){
          case YOUR_ITEMS:
                if (spanIndex == 0) {
                    outRect.left = 10;
                    outRect.right = 5;
                } else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
                    outRect.left = 5;
                    outRect.right = 10;
                }
        }
    }

}
person Antikvo    schedule 10.09.2015

Я решил это сам, установив поле элемента и заполнение RecyclerView. И поля, и отступы составляют половину ожидаемого пространства, поэтому проблема исчезла.

person outofmemory    schedule 08.03.2015

Можно установить отступы для двух сторон RecyclerView:

myRecyclerView.setPadding(10,0,10,0);

Таким образом можно выровнять пространство по бокам и между колоннами.

person Gabriel Cséfalvay    schedule 06.07.2018

Установка разного левого/правого пространства в ItemDecoration в соответствии с spanindex хорошо работает только для представления элемента с фиксированным размером. Если представление элемента содержит представление изображения, установленное в обертке_содержимого по высоте, представление элемента может изменить положение диапазона, но индекс диапазона не обновляется, как вы хотите, поля будут беспорядочными.

Мой способ заключается в использовании симметричного левого/правого пространства для элемента просмотра.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // init
    recyclerView = (RecyclerView) rv.findViewById(R.id.img_waterfall);
    layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(new MyAdapter(getContext()));
    recyclerView.addItemDecoration(new SpacesItemDecoration(
        getResources().getDimensionPixelSize(R.dimen.space)));
}


private static class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private final int space;
    public SpacesItemDecoration(int space) {
        this.space = space;
    }
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                               RecyclerView.State state) {
        outRect.bottom = 2 * space;
        int pos = parent.getChildAdapterPosition(view);
        outRect.left = space;
        outRect.right = space;
        if (pos < 2)
            outRect.top = 2 * space;
    }
}

Затем установите такое же заполнение для RecylerView(left/right) android:paddingLeft="@dimen/space" android:paddingRight="@dimen/space"

    <android.support.v7.widget.RecyclerView
        android:id="@+id/img_waterfall"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/space"
        android:paddingRight="@dimen/space"/>
person emotionfxxk    schedule 05.05.2016

Я бы немного изменил и изменил оба ответа на относительный вопрос

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private final int mSpace;

    public SpacesItemDecoration(int space) {
        this.mSpace = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        if (spanIndex == 0) {
            outRect.left = 30;
            outRect.right = 15;
        } else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
            outRect.left = 15;
            outRect.right = 30;
        }
        outRect.bottom = 30;
        // Add top margin only for the first item to avoid double space between items
        if (parent.getChildAdapterPosition(view) == 0) {
            outRect.top = 30;
            outRect.right = 30;
        }
    }
}
person Hardy    schedule 19.01.2016

Я решил это в 3 шага следующим образом:

  1. Добавьте marginStart и marginEnd к родителю (RecyclerView) с тем же значением
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginEnd="@dimen/margin_small"
    app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
    app:spanCount="2" />
  1. Создайте собственный класс ItemDecorator и расширите RecyclerView.ItemDecoratior. Получить родительские поля, а затем изменить внутренние поля в половине каждого дочернего элемента в зависимости от случая.
class SpaceItemDecorator : RecyclerView.ItemDecoration() {

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

        val layoutParams = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
        val spanIndex = layoutParams.spanIndex

        if (spanIndex == 0) {
            val marginParentEnd = parent.marginEnd
            layoutParams.marginEnd = marginParentEnd / 2
        } else {
            val marginParentStart = parent.marginStart
            layoutParams.marginStart = marginParentStart / 2
        }

        view.layoutParams = layoutParams
    }
}
  1. Добавьте пользовательский ItemDecorator в recyclerView.
recyclerview.addItemDecoration(SpaceItemDecorator())

Вот и все, без хардкода полей/отступов и в Koltin xD

Примечание. Важно не устанавливать никаких горизонтальных полей в макете дочернего представления.

person Gabriel Perez    schedule 12.02.2020

Следующий код обрабатывает StaggeredGridLayout без проблем (по крайней мере, исходя из моего опыта). Вам нужно только указать интервал для SpaceItemDecorator.

class SpaceItemDecoration(private val spacing: Int) :
    RecyclerView.ItemDecoration() {

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

        val lm = parent.layoutManager as StaggeredGridLayoutManager
        val lp = view.layoutParams as StaggeredGridLayoutManager.LayoutParams

        val spanCount = lm.spanCount
        val spanIndex = lp.spanIndex
        val positionLayout = lp.viewLayoutPosition
        val itemCount = lm.itemCount
        val position = parent.getChildAdapterPosition(view)

        outRect.right = spacing / 2
        outRect.left = spacing / 2
        outRect.top = spacing / 2
        outRect.bottom = spacing / 2

        if (spanIndex == 0) outRect.left = spacing

        if (position < spanCount) outRect.top = spacing

        if (spanIndex == (spanCount - 1)) outRect.right = spacing

        if (positionLayout > (itemCount - spanCount)) outRect.bottom = spacing
    }
}

Вы можете получить интервал в пикселях следующим образом:

 val spacingInPixels = resources.getDimensionPixelSize(R.dimen.grid_layout_margin)

А затем вам просто нужно назначить украшение предмета вашему представлению ресайклера:

recycler_view.addItemDecoration(
                  SpaceItemDecoration(
                        spacingInPixels
                    )
                )
person Tatjana Djurdjevic    schedule 28.03.2020