Пересечение столбцов GridLayoutManager Android Recyclerview

Как установить расстояние между столбцами с помощью RecyclerView с помощью GridLayoutManager? Установка поля / заполнения внутри моего макета не влияет.

RecyclerViews поддерживают концепцию ItemDecoration : специальные смещения и рисование вокруг каждого элемента. Как видно из этого ответа , вы можете использовать

 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 item to avoid double space between items if (parent.getChildLayoutPosition(view) == 0) { outRect.top = space; } else { outRect.top = 0; } } } 

Затем добавьте его через

 mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view); int spacingInPixels = getResources().getDimensionPixelSize(R.dimen.spacing); mRecyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels)); 

Следующий код работает хорошо, и каждый столбец имеет одинаковую ширину:

 public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view); // item position int column = position % spanCount; // item column if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } } 

Применение

1. без края

введите описание изображения здесь

 int spanCount = 3; // 3 columns int spacing = 50; // 50px boolean includeEdge = false; recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge)); 

2. с краем

введите описание изображения здесь

 int spanCount = 3; // 3 columns int spacing = 50; // 50px boolean includeEdge = true; recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge)); 

Следующее – пошаговое простое решение, если вы хотите равное расстояние вокруг элементов и равные размеры элементов.

ItemOffsetDecoration

 public class ItemOffsetDecoration extends RecyclerView.ItemDecoration { private int mItemOffset; public ItemOffsetDecoration(int itemOffset) { mItemOffset = itemOffset; } public ItemOffsetDecoration(@NonNull Context context, @DimenRes int itemOffsetId) { this(context.getResources().getDimensionPixelSize(itemOffsetId)); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.set(mItemOffset, mItemOffset, mItemOffset, mItemOffset); } } 

Реализация

В исходном коде добавьте ItemOffsetDecoration в свой RecyclerView. Значение смещения позиции должно быть половинным размером фактического значения, которое вы хотите добавить как пространство между элементами.

 mRecyclerView.setLayoutManager(new GridLayoutManager(context, NUM_COLUMNS); ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(context, R.dimen.item_offset); mRecyclerView.addItemDecoration(itemDecoration); 

Кроме того, установите значение смещения позиции в качестве дополнения для своего RecyclerView и укажите android:clipToPadding=false .

  

Попробуй это. Он позаботится о равном расстоянии друг от друга. Работает как с List, Grid, так и с StaggeredGrid.

отредактированный

Обновленный код должен обрабатывать большинство угловых случаев с пролетами, ориентацией и т. Д. Обратите внимание, что при использовании setSpanSizeLookup () с GridLayoutManager параметр setSpanIndexCacheEnabled () рекомендуется по соображениям производительности.

Заметьте, похоже, что с StaggeredGrid, похоже, есть ошибка, когда индекс детей становится глупо и трудно отслеживать, поэтому приведенный ниже код может не очень хорошо работать с StaggeredGridLayoutManager.

 public class ListSpacingDecoration extends RecyclerView.ItemDecoration { private static final int VERTICAL = OrientationHelper.VERTICAL; private int orientation = -1; private int spanCount = -1; private int spacing; private int halfSpacing; public ListSpacingDecoration(Context context, @DimenRes int spacingDimen) { spacing = context.getResources().getDimensionPixelSize(spacingDimen); halfSpacing = spacing / 2; } public ListSpacingDecoration(int spacingPx) { spacing = spacingPx; halfSpacing = spacing / 2; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (orientation == -1) { orientation = getOrientation(parent); } if (spanCount == -1) { spanCount = getTotalSpan(parent); } int childCount = parent.getLayoutManager().getItemCount(); int childIndex = parent.getChildAdapterPosition(view); int itemSpanSize = getItemSpanSize(parent, childIndex); int spanIndex = getItemSpanIndex(parent, childIndex); /* INVALID SPAN */ if (spanCount < 1) return; setSpacings(outRect, parent, childCount, childIndex, itemSpanSize, spanIndex); } protected void setSpacings(Rect outRect, RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { outRect.top = halfSpacing; outRect.bottom = halfSpacing; outRect.left = halfSpacing; outRect.right = halfSpacing; if (isTopEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { outRect.top = spacing; } if (isLeftEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { outRect.left = spacing; } if (isRightEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { outRect.right = spacing; } if (isBottomEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { outRect.bottom = spacing; } } @SuppressWarnings("all") protected int getTotalSpan(RecyclerView parent) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanCount(); } else if (mgr instanceof StaggeredGridLayoutManager) { return ((StaggeredGridLayoutManager) mgr).getSpanCount(); } else if (mgr instanceof LinearLayoutManager) { return 1; } return -1; } @SuppressWarnings("all") protected int getItemSpanSize(RecyclerView parent, int childIndex) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex); } else if (mgr instanceof StaggeredGridLayoutManager) { return 1; } else if (mgr instanceof LinearLayoutManager) { return 1; } return -1; } @SuppressWarnings("all") protected int getItemSpanIndex(RecyclerView parent, int childIndex) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount); } else if (mgr instanceof StaggeredGridLayoutManager) { return childIndex % spanCount; } else if (mgr instanceof LinearLayoutManager) { return 0; } return -1; } @SuppressWarnings("all") protected int getOrientation(RecyclerView parent) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof LinearLayoutManager) { return ((LinearLayoutManager) mgr).getOrientation(); } else if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getOrientation(); } else if (mgr instanceof StaggeredGridLayoutManager) { return ((StaggeredGridLayoutManager) mgr).getOrientation(); } return VERTICAL; } protected boolean isLeftEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (orientation == VERTICAL) { return spanIndex == 0; } else { return (childIndex == 0) || isFirstItemEdgeValid((childIndex < spanCount), parent, childIndex); } } protected boolean isRightEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (orientation == VERTICAL) { return (spanIndex + itemSpanSize) == spanCount; } else { return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex); } } protected boolean isTopEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (orientation == VERTICAL) { return (childIndex == 0) || isFirstItemEdgeValid((childIndex < spanCount), parent, childIndex); } else { return spanIndex == 0; } } protected boolean isBottomEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (orientation == VERTICAL) { return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex); } else { return (spanIndex + itemSpanSize) == spanCount; } } protected boolean isFirstItemEdgeValid(boolean isOneOfFirstItems, RecyclerView parent, int childIndex) { int totalSpanArea = 0; if (isOneOfFirstItems) { for (int i = childIndex; i >= 0; i--) { totalSpanArea = totalSpanArea + getItemSpanSize(parent, i); } } return isOneOfFirstItems && totalSpanArea <= spanCount; } protected boolean isLastItemEdgeValid(boolean isOneOfLastItems, RecyclerView parent, int childCount, int childIndex, int spanIndex) { int totalSpanRemaining = 0; if (isOneOfLastItems) { for (int i = childIndex; i < childCount; i++) { totalSpanRemaining = totalSpanRemaining + getItemSpanSize(parent, i); } } return isOneOfLastItems && (totalSpanRemaining <= spanCount - spanIndex); } } 

Надеюсь, поможет.

Следующий код будет обрабатывать 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; } } 

Затем используйте его

 mRecyclerView.addItemDecoration(new SpacesItemDecoration(mMargin)); 

Вот решение, которое не требует «spanCount» (количество столбцов), которое я использую, потому что я использую GridAutofitLayoutManager (вычисляет количество столбцов в соответствии с размером ячейки)

(будьте осторожны, что это будет работать только на GridLayoutManager )

 public class GridSpacesItemDecoration extends RecyclerView.ItemDecoration { private final boolean includeEdge; private int spacing; public GridSpacesItemDecoration(int spacing, boolean includeEdge) { this.spacing = spacing; this.includeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (parent.getLayoutManager() instanceof GridLayoutManager) { GridLayoutManager layoutManager = (GridLayoutManager)parent.getLayoutManager(); int spanCount = layoutManager.getSpanCount(); int position = parent.getChildAdapterPosition(view); // item position int column = position % spanCount; // item column if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } } } 

Вот GridAutofitLayoutManager – это кто-то заинтересован:

 public class GridAutofitLayoutManager extends GridLayoutManager { private int mColumnWidth; private boolean mColumnWidthChanged = true; public GridAutofitLayoutManager(Context context, int columnWidth) { /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1); setColumnWidth(checkedColumnWidth(context, columnWidth)); } public GridAutofitLayoutManager(Context context,int unit, int columnWidth) { /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1); int pixColumnWidth = (int) TypedValue.applyDimension(unit, columnWidth, context.getResources().getDisplayMetrics()); setColumnWidth(checkedColumnWidth(context, pixColumnWidth)); } public GridAutofitLayoutManager(Context context, int columnWidth, int orientation, boolean reverseLayout) { /* Initially set spanCount to 1, will be changed automatically later. */ super(context, 1, orientation, reverseLayout); setColumnWidth(checkedColumnWidth(context, columnWidth)); } 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()); } return columnWidth; } public void setColumnWidth(int newColumnWidth) { if (newColumnWidth > 0 && newColumnWidth != mColumnWidth) { mColumnWidth = newColumnWidth; mColumnWidthChanged = true; } } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { int width = getWidth(); int height = getHeight(); if (mColumnWidthChanged && mColumnWidth > 0 && width > 0 && height > 0) { 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; } super.onLayoutChildren(recycler, state); } } 

В заключение:

 mDevicePhotosView.setLayoutManager(new GridAutofitLayoutManager(getContext(), getResources().getDimensionPixelSize(R.dimen.item_size))); mDevicePhotosView.addItemDecoration(new GridSpacesItemDecoration(Util.dpToPx(getContext(), 2),true)); 

Скопированный @edwardaa предоставил код, и я делаю его идеальным для поддержки RTL:

 public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; private int headerNum; private boolean isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL; public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge, int headerNum) { this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; this.headerNum = headerNum; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view) - headerNum; // item position if (position >= 0) { int column = position % spanCount; // item column if(isRtl) { column = spanCount - 1 - column; } if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } else { outRect.left = 0; outRect.right = 0; outRect.top = 0; outRect.bottom = 0; } } } 

Если вы хотите ИЗМЕНИТЬ размер вашего элемента RecyclerView на всех устройствах. Вы можете сделать это

 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

  ...  

dimens.xml

  60dp 

Мероприятия

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

введите описание изображения здесь введите описание изображения здесь

В приведенных выше ответах были уточнены способы установки обработки полей GridLayoutManager и LinearLayoutManager.

Но для StaggeredGridLayoutManager ответ Пирдад Сахизады гласит: «Это может не сработать с StaggeredGridLayoutManager». Это должна быть проблема с IndexOfSpan.

Вы можете получить его таким образом:

 private static class MyItemDecoration extends RecyclerView.ItemDecoration { @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int index = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex(); } } 

Существует только одно простое решение, которое вы можете запомнить и реализовать там, где это необходимо. Никаких ошибок, никаких сумасшедших расчетов. Поместите маржу в макет карты / позиции и поместите тот же размер, что и дополнение к RecyclerView:

item_layout.xml

  

activity_layout.xml

  

Для этой проблемы существует очень простое и вместе с тем гибкое решение, использующее только XML, который работает на каждом LayoutManager.

Предположим, вы хотите равное расстояние X (например, 8dp).

  1. Оберните элемент CardView в другой макет

  2. Дайте внешнему макету дополнение X / 2 (4dp)

  3. Сделать внешний макет фона прозрачным

     
  1. Дайте вашему RecyclerView дополнение X / 2 (4dp)

  

вот и все. У вас идеальный интервал X (8dp).

 public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); int column = params.getSpanIndex(); if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } } 

Немного отличается от ответа edwardaa, разница в том, как определяется столбец, потому что в таких случаях, как элементы с разной высотой, столбец не может быть определен просто% spanCount

Вот моя модификация SpacesItemDecoration которая может принимать numOfColums и пространство одинаково сверху, снизу, слева и справа .

 public class SpacesItemDecoration extends RecyclerView.ItemDecoration { private int space; private int mNumCol; public SpacesItemDecoration(int space, int numCol) { this.space = space; this.mNumCol=numCol; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { //outRect.right = space; outRect.bottom = space; //outRect.left = space; //Log.d("ttt", "item position" + parent.getChildLayoutPosition(view)); int position=parent.getChildLayoutPosition(view); if(mNumCol<=2) { if (position == 0) { outRect.left = space; outRect.right = space / 2; } else { if ((position % mNumCol) != 0) { outRect.left = space / 2; outRect.right = space; } else { outRect.left = space; outRect.right = space / 2; } } }else{ if (position == 0) { outRect.left = space; outRect.right = space / 2; } else { if ((position % mNumCol) == 0) { outRect.left = space; outRect.right = space/2; } else if((position % mNumCol) == (mNumCol-1)){ outRect.left = space/2; outRect.right = space; }else{ outRect.left=space/2; outRect.right=space/2; } } } if(position 

и используйте ниже код по вашей логике.

 recyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels, numCol)); 

В итоге я сделал это для моего RecyclerView с GridLayoutManager и HeaderView .

В приведенном ниже коде я устанавливаю расстояние 4dp между каждым элементом (2dp вокруг каждого отдельного элемента и 2dp отступы вокруг всего recyclerview).

layout.xml

  

fragment / активность

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

спасибо ответ edwardaa https://stackoverflow.com/a/30701422/2227031

Еще один момент, который следует отметить, заключается в следующем:

если общий интервал и общая ширина элемента не равны ширине экрана, вам также необходимо настроить itemWidth, например, на адаптер onBindViewHolder метод

 Utils.init(_mActivity); int width = 0; if (includeEdge) { width = ScreenUtils.getScreenWidth() - spacing * (spanCount + 1); } else { width = ScreenUtils.getScreenWidth() - spacing * (spanCount - 1); } int itemWidth = width / spanCount; ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) holder.imageViewAvatar.getLayoutParams(); // suppose the width and height are the same layoutParams.width = itemWidth; layoutParams.height = itemWidth; holder.imageViewAvatar.setLayoutParams(layoutParams); 

Чтобы выполнить 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; } 

Эта ссылка работала для меня во всех ситуациях, когда вы можете попробовать это.

Котлинская версия, которую я сделал на основе великого ответа 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 } } 

Если у вас есть тумблер, который переключает между списком в сетку, не забудьте вызвать recyclerView.removeItemDecoration() перед установкой любого нового украшения Item. Если нет, то новые вычисления для интервала будут неверными.


Что-то вроде этого.

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

Это будет работать и для RecyclerView с заголовком.

 public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; private int headerNum; public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge, int headerNum) { this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; this.headerNum = headerNum; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view) - headerNum; // item position if (position >= 0) { int column = position % spanCount; // item column if (includeEdge) { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) if (position >= spanCount) { outRect.top = spacing; // item top } } } else { outRect.left = 0; outRect.right = 0; outRect.top = 0; outRect.bottom = 0; } } } } 
Давайте будем гением компьютера.