322

如何使用GridLayoutManager设置RecyclerView的列间距?在布局中设置边距/填充无效。

4

33个答案33

重置为默认值
559

以下代码运行良好,并且每个列的宽度相同:

公共类GridSpacingItemDecoration扩展了RecyclerView。项目装饰{私有int spanCount;私有int间距;private boolean includeEdge;public GridSpacingItemDecoration(int spanCount,int spacing,boolean includeEdge){this.spanCount=span计数;this.space=间距;this.includeEdge=包含边缘;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){int position=parent.getChildAdapterPosition(视图);//项目位置int column=位置%spanCount;//项目列if(包括Edge){outRect.left=间距-列*间距/spanCount;//间距-列*((1f/spanCount)*间距)outRect.right=(column+1)*间距/spanCount;//(列+1)*((1f/spanCount)*间距)如果(位置<spanCount){//上边缘outRect.top=间距;}outRect.bottom=间距;//项目底部}其他{outRect.left=列*间距/spanCount;//列*((1f/spanCount)*间距)outRect.right=间距-(列+1)*间距/spanCount;//间距-(列+1)*((1f/spanCount)*间距)if(位置>=spanCount){outRect.top=间距;//项目顶部}}}}

用法

1.无边缘

在此处输入图像描述

int span计数=3;//3列int间距=50;//50像素boolean includeEdge=false;recyclerView.addItemDecoration(新的GridSpacingItemDecorsion(spanCount,spacing,includeEdge));

2.带边缘

在此处输入图像描述

int span计数=3;//3列int间距=50;//50像素boolean includeEdge=true;recyclerView.addItemDecoration(新的GridSpacingItemDecorsion(spanCount,spacing,includeEdge));
19
  • 21
    除非项目具有不同的跨距(如标题),否则可以正常工作。
    – 马修
    评论 2015年7月1日17:51
  • 11
    伟大的回答;一个提示:间距以px为单位(因此可以使用Math.round(someDpValue*getResources().getDisplayMetrics().density)将dp转换为px))
    – 李兆光
    评论 2016年3月3日19:40
  • 它工作正常,但我有一个问题,我使用的是GridLayoutManager,spanCount为2(默认值),但用户可以更改spanCount,因此当spanCount从默认位置更改时,某些位置上的填充更为可见,例如spanCount将为3,而不是2,38,912,13等上的填充/边距。 评论 2016年3月15日8:13
  • 效果很好!但我在使用StaggeredGridLayoutManager时遇到了一些问题。imgur.com/XVutH5u水平边距有时会有所不同。 评论 2016年3月25日10:38
  • 6
    当布局为rtl(2列或更多)时,这不起作用。当前,在rtl模式下,列之间的间距不正确。当outRect.left位于rtl中时,需要将其替换为outRect.right。 评论 2017年7月17日6:15
395

RecyclerViews支持项目装饰:每个元素周围的特殊偏移和绘制。如中所示这个答案,您可以使用

公共类SpacesItemDecoration扩展了RecyclerView。项目装饰{私有int空间;公共空间项目装饰(int space){this.space=空格;}@覆盖public void getItemOffsets(Rect outRect,查看视图,RecyclerView父级,Recycler视图。州-州){outRect.left=空格;outRect.right=空间;outRect.bottom=空格;//仅为第一个项目添加上边距,以避免项目之间出现双重空格if(parent.getChildLayoutPosition(view)==0){outRect.top=空格;}其他{outRect.top=0;}}}

然后通过添加

mRecyclerView=(回收视图)rootView.findViewById(R.id.my_recycler_view);int spacingInPixels=getResources().getDimensionPixelSize(R.dimen.spacing);mRecyclerView.addItemDecoration(新的SpacesItemDecorsion(像素间距));
14
  • 如果您不想弄乱“if for the first position”,请使用“outRect.top=space”并删除“outRec.bottom”。;-] 评论 2015年4月20日18:01
  • 32
    @ianhanniballake,虽然这在使用单跨布局管理器时有效,但在使用多跨布局管理程序时失败。 评论 2015年7月21日13:42
  • 6
    如果你用GridLayoutManager这样做-第二、第三个的所有第一项。。。第n列将停留在顶部(因为没有空格)。所以我认为最好是这样做。top=space/2和.bottom=space/2。 评论 2016年2月10日23:55
  • 5
    这个答案并没有回答最初的问题。问题的重点是网格布局管理器。答案对多列/行布局无效 评论 2019年5月13日19:25
  • 你没有考虑跨度
    – 用户924
    评论 2020年3月25日13:42
92

如果您希望项目周围的间距相等,项目大小相等,那么以下是一个简单的分步解决方案。

项目抵消装饰

公共类ItemOffsetDecoration扩展了RecyclerView。项目装饰{私有int mItemOffset;公共项目偏移装饰(int itemOffset){mItemOffset=项目偏移;}public ItemOffsetDecoration(@NonNull上下文,@DimenRes int itemOffsetId){this(context.getResources().getDimensionPixelSize(itemOffsetId));}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView parent,回收站视图。州-州){super.getItemOffsets(outRect,view,parent,state);outRect.集合(mItemOffset、mItemOffest、mItemOffset、mItemOffset);}}

实施

在源代码中,添加项目抵消装饰到您的回收站视图。项目偏移量值应该是要添加为项目之间空格的实际值的一半。

mRecyclerView.setLayoutManager(新的网格布局管理器(context,NUM_COLUMNS);ItemOffsetDecoration itemDecoration=新ItemOffsetDecoration(上下文,R.dimen.item_offset);mRecyclerView.addItemDecoration(itemDecoration);

此外,将项目偏移值设置为其填充回收站视图,并指定android:clipToPadding=false.

<android.support.v7.widget。回收站视图android:id=“@+id/recyclerview_grid”android:layout_width=“匹配租金”android:layout_height=“match_parent”android:clipToPadding=“false”android:padding=“@dimen/item_offset”/>
1
  • 1
    完美,简单有效。
    – B.灌木
    评论 2019年4月16日5:33
35

试试这个。它会注意到周围的间距相等。同时适用于List、Grid和StaggeredGrid。

已编辑

更新后的代码应处理大多数具有跨距、方向等的拐角情况。请注意,如果将setSpanSizeLookup()与GridLayoutManager一起使用,出于性能原因,建议设置setSpanIndexCacheEnabled()。

请注意,在使用StaggeredGrid时,似乎存在一个错误,其中子项的索引变得古怪且难以跟踪,因此下面的代码可能无法与Staggered GridLayoutManager一起很好地工作。

公共类ListSpacingDecoration扩展了RecyclerView。项目装饰{private static final int VERTICAL=OrientationHelper。垂直;私有int方向=-1;private int spanCount=-1;私有int间距;private int半间距;public ListSpacingDecoration(上下文上下文,@DimenRs-int-spacingDimen){spacing=context.getResources().getDimensionPixelSize(spacingDimen);半间距=间距/2;}公共列表间距装饰(int spacingPx){间距=间距Px;半间距=间距/2;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){super.getItemOffsets(outRect,view,parent,state);if(方向==-1){orientation=getOrientation(父级);}如果(spanCount==-1){spanCount=getTotalSpan(父级);}int childCount=父项.getLayoutManager().getItemCount();int childIndex=parent.getChildAdapterPosition(视图);int itemSpanSize=getItemSpanSize(父项,子索引);int spanIndex=getItemSpanIndex(父项,子索引);/*无效跨度*/如果(spanCount<1)返回;setSpacings(outRect、parent、childCount、childIndex、itemSpanSize、spanIndex);}受保护的void setSpacings(Rect outRect,RecyclerView parent,int childCount,int child Index,int itemSpanSize,int spanIndex){outRect.top=半间距;outRect.bottom=半间距;outRect.left=半间距;outRect.right=半间距;if(isTopEdge(parent、childCount、childIndex、itemSpanSize、spanIndex)){outRect.top=间距;}if(isLeftEdge(parent、childCount、childIndex、itemSpanSize、spanIndex)){outRect.left=间距;}if(isRightEdge(parent、childCount、childIndex、itemSpanSize、spanIndex)){outRect.right=间距;}if(isBottomEdge(parent、childCount、childIndex、itemSpanSize、spanIndex)){outRect.bottom=间距;}}@禁止警告(“全部”)protected int getTotalSpan(RecyclerView父级){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(GridLayoutManager的mgr实例){return((GridLayoutManager)管理器).getSpanCount();}else if(交错网格布局管理器的mgr实例){return((交错网格布局管理器)mgr).getSpanCount();}else if(LinearLayoutManager的mgr实例){返回1;}返回-1;}@禁止警告(“全部”)protected int getItemSpanSize(RecyclerView父级,int childIndex){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(GridLayoutManager的mgr实例){return((GridLayoutManager)mgr).getSpanSizeLookup().getSpranSize(childIndex);}else if(交错网格布局管理器的mgr实例){返回1;}else if(线性布局管理器的mgr实例){返回1;}返回-1;}@禁止警告(“全部”)protected int getItemSpanIndex(RecyclerView父级,int childIndex){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(GridLayoutManager的mgr实例){return((GridLayoutManager)mgr).getSpanSizeLookup().getSpranIndex(childIndex,spanCount);}else if(交错网格布局管理器的mgr实例){return childIndex%spanCount;}else if(线性布局管理器的mgr实例){返回0;}返回-1;}@禁止警告(“全部”)protected int getOrientation(RecyclerView父级){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(LinearLayoutManager的mgr实例){return((LinearLayoutManager)管理器).getOrientation();}else if(GridLayoutManager的mgr实例){return((GridLayoutManager)管理器).getOrientation();}else if(交错网格布局管理器的mgr实例){return((StaggeredGridLayoutManager)管理器).getOrientation();}返回VERTICAL;}受保护的布尔值isLeftEdge(RecyclerView父级,int childCount,int child Index,int itemSpanSize,int spanIndex){if(方向==垂直){返回span索引==0;}其他{return(childIndex==0)||isFirstItemEdgeValid((childIndex<spanCount),parent,childIndex);}}protected boolean isRightEdge(RecyclerView父级,int childCount,int childIndex,int itemSpanSize,int spanIndex){if(方向==垂直){return(spanIndex+itemSpanSize)==spanCount;}其他{return isLastItemEdgeValid((childIndex>=childCount-spanCount),parent,childCount,childIndex,spanIndex);}}受保护的布尔值isTopEdge(RecyclerView父级,int childCount,int child Index,int itemSpanSize,int spanIndex){if(方向==垂直){return(childIndex==0)||isFirstItemEdgeValid((childIndex<spanCount),parent,childIndex);}其他{return span指数==0;}}protected boolean isBottomEdge(RecyclerView父级,int childCount,int childIndex,int itemSpanSize,int spanIndex){if(方向==垂直){return isLastItemEdgeValid((childIndex>=childCount-spanCount),parent,childCount,childIndex,spanIndex);}其他{return(spanIndex+itemSpanSize)==spanCount;}}受保护的布尔值isFirstItemEdgeValid(布尔值isOneOfFirstItems,RecyclerView父级,int childIndex){int totalSpanArea=0;if(是OneOfFirstItems){for(int i=childIndex;i>=0;i--){totalSpanArea=总跨度面积+getItemSpanSize(父项,i);}}return是OneOfFirstItems&&totalSpanArea<=spanCount;}受保护的布尔值isLastItemEdgeValid(布尔值isOneOfLastItems,RecyclerView父级,int childCount,int child Index,int spanIndex){int totalSpanRemaining=0;if(是最后一项之一){for(int i=childIndex;i<childCount;i++){totalSpanRemaining=totalSpan Remaining+getItemSpanSize(父项,i);}}return isOneOfLastItems&&(totalSpanRemaining<=spanCount-spanIndex);}}

希望能有所帮助。

7
  • 1
    我在第一行项目后面有双跨度。发生这种情况是因为parent.getChildCount()为第一项返回1,为第二项返回2,依此类推。所以,我建议为顶部边缘的项添加空格,例如:outRect.top=childIndex<spanCount?像素间距:0;并为每个项目添加底部空间:outRect.bottom=spacingInPixels;
    – 伊凡·P
    评论 2015年5月24日12:53
  • 滚动RecyclerView时,间距发生了变化。
    – 尤格什
    评论 2015年5月25日7:30
  • 我认为parent.getChildCount()应该更改为“parent.getLayoutManager().getItemCount(”。此外,isBottomEdge函数需要更改为“return childIndex>=childCount-spanCount+spanIndex”。在改变这些之后,我得到了相等的间距。但请注意,如果跨距计数大于2,则此解决方案不会为我提供相等的项目大小,因为偏移值因位置而异。 评论 2015年5月27日2:31
  • 1
    @yqritc感谢您伤害了parent.getChildCount()。我已经更新了我的答案以使用parent.getLayoutManager().getItemCount() 评论 2015年12月23日20:08
  • 2
    即使在可变跨度的情况下,这也很好地实现了开箱即用,祝贺并感谢您! 评论 2017年9月27日15:34
34

只有一个简单的解决方案,您可以记住并在需要时实施。没有错误,没有疯狂的计算。在卡片/项目布局中添加边距,并在回收站视图中添加与填充大小相同的边距:

项目_布局.xml

<CardViewandroid:layout_width=“匹配租金”android:layout_height=“wrap_content”android:margin=“10dp”>

activity_layout.xml

<回收站视图android:layout_width=“匹配租金”android:layout_height=“wrap_content”android:padding=“10dp”/>

更新: 在此处输入图像描述

7
  • 它工作得很好!请你详细说明一下这个问题好吗? 评论 2019年10月11日17:03
  • 非常感谢!我在寻找一些技术原因,为什么需要在回收商的填充物和项目的利润之间进行这种合作。无论如何,你为我做了这么多。 评论 2019年10月15日7:13
  • 完美的解决方案! 评论 2021年1月25日10:02
  • 兄弟,你真是天才。 评论 2021年10月20日22:11
  • 这是怎么一回事?没有格栅的简单垂直RV? 评论 2021年12月14日18:10
23

以下代码将处理StaggeredGridLayoutManager、GridLayotManager和LinearLayoutManager。

公共类SpacesItemDecoration扩展了RecyclerView。项目装饰{private int halfSpace;公共空间项目装饰(内部空间){this.halfSpace=空格/2;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){if(parent.getPaddingLeft()!=半空间){parent.setPadding(半空间、半空间、半空、半空间);parent.setClipToPadding(false);}outRect.top=半空格;outRect.bottom=半空格;outRect.left=半空间;outRect.right=半空间;}}

然后使用它

mRecyclerView.addItemDecoration(新空间ItemDecorsion(mMargin));
7
  • 1
    这是最简单的一个。重要的一点是,您还必须在xml中向父级添加填充。就我而言,它是这样工作的。谢谢。
    – 萨米尔
    评论 2015年8月31日9:32
  • 这个SpaceItem装饰实际上将填充添加到父级(recycler视图)。 评论 2015年8月31日15:23
  • 只有半空间当我没有在xml中将填充设置为父级时,填充显示在(右侧)
    – 萨米尔
    评论 2015年9月1日6:51
  • 只是右边少了?这可能是因为xml中已经在左侧设置了一半空间作为leftPadding,并且此代码仅检查是否在RecyclerView上设置了左填充。 评论 2015年9月1日22:03
  • 嗯,我在xml中没有设置任何填充。
    – 萨米尔
    评论 2015年9月2日7:03
13

这里有一个解决方案不需要“spanCount”(列数)我用它是因为我用网格自动调整布局管理器(根据所需的单元格大小计算列数)

(注意,这只适用于网格布局管理器)

公共类GridSpacesItemDecoration扩展了RecyclerView。项目装饰{private final boolean includeEdge;私有int间距;public GridSpacesItemDecoration(int间距,boolean includeEdge){this.space=间距;this.includeEdge=包含边缘;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){if(GridLayoutManager的parent.getLayoutManager()实例){GridLayoutManager layoutManager=(GridLayotManager)父项.getLayoutManager();int spanCount=布局管理器.getSpanCount();int position=parent.getChildAdapterPosition(视图);//项目位置int column=位置%spanCount;//项目列if(包括Edge){outRect.left=间距-列*间距/spanCount;//间距-列*((1f/spanCount)*间距)outRect.right=(column+1)*间距/spanCount;//(列+1)*((1f/spanCount)*间距)if(位置<spanCount){//上边缘outRect.top=间距;}outRect.bottom=间距;//项目底部}其他{outRect.left=列*间距/spanCount;//列*((1f/spanCount)*间距)outRect.right=间距-(列+1)*间距/span计数;//间距-(列+1)*((1f/spanCount)*间距)if(位置>=spanCount){outRect.top=间距;//项目顶部}}}}}

这里是网格自动调整布局管理器是否有人感兴趣:

公共类GridAutofitLayoutManager扩展了GridLayoutManager{private int mColumnWidth;私有布尔值mColumnWidthChanged=true;public GridAutofitLayoutManager(上下文上下文,int columnWidth){/*最初将spanCount设置为1,稍后将自动更改*/超(上下文,1);setColumnWidth(checkedColumnWidth(context,columnWith));}public GridAutofitLayoutManager(上下文上下文,int unit,int columnWidth){/*最初将spanCount设置为1,稍后将自动更改*/超(上下文,1);int pixColumnWidth=(int)TypedValue.applyDimension(unit,columnWith,context.getResources().getDisplayMetrics());setColumnWidth(checkedColumn宽度(context,pixColumnWidth));}public GridAutofitLayoutManager(上下文上下文,int columnWidth,int orientation,boolean reverseLayout){/*最初将spanCount设置为1,稍后将自动更改*/super(context,1,orientation,reverseLayout);setColumnWidth(checkedColumnWidth(context,columnWith));}private int checkedColumnWidth(上下文上下文,int columnWith){if(列宽<=0){/*设置默认的columnWidth值(此处为48dp)。最好移动这个常数在顶部设置为静态常量,但我们需要上下文将其转换为dp,所以不能这样做*/columnWidth=(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,48,context.getResources().getDisplayMetrics());}return columnWidth;返回列宽度;}公共void setColumnWidth(int newColumnWidth){if(newColumnWidth>0&&newColumn Width!=m列宽度){mColumnWidth=newColumnWidth;mColumnWidthChanged=真;}}@覆盖公共void onLayoutChildren(RecyclerView.Recycler回收器,Recycler-View.State状态){int width=获取宽度();int height=getHeight();if(mColumnWidthChanged&&mColumnWidth>0&&width>0&&height>0){int totalSpace;if(getOrientation()==垂直){totalSpace=宽度-getPaddingRight()-getPadingLeft();}其他的{totalSpace=高度-getPaddingTop()-getPadingBottom();}int spanCount=数学最大值(1,totalSpace/mColumnWidth);设置跨度计数(spanCount);mColumnWidthChanged=假;}super.onLayoutChildren(州回收商);}}

最后:

mDevicePhotosView.setLayoutManager(新的网格自动布局管理器(getContext(),getResources().getDimensionPixelSize(R.dimen.item_size)));mDevicePhotosView.addItemDecoration(新的GridSpacesItemDecorsion(Util.dpToPx(getContext(),2),true));
2
  • 你好。这很好,但我在你的解决方案中使用了一个标题。你能建议如何实现全宽页眉吗?
    – 阿吉特
    评论 2017年3月23日8:06
  • 请您按照以下方式与版面经理核实职位layoutManager.getPosition(视图)然后检查位置是否为零,这将是您的标题。。此外,这种方式还可以在您想要的任何位置添加另一个页眉:) 评论 2019年11月10日23:55
8

如果你想固定的你的尺寸回收站视图所有设备中的项目。你可以这样做

公共类GridSpacingItemDecoration扩展了RecyclerView。项目装饰{私有int mSpanCount;私有浮点mItemSize;public GridSpacingItemDecoration(int spanCount,int itemSize){this.mSpanCount=span计数;mItemSize=项大小;}@覆盖public void getItemOffsets(final Rect outRect,final View View,RecyclerView parent,回收站视图。州-州){最终int位置=parent.getChildLayoutPosition(视图);最终int列=位置%mSpanCount;final int parentWidth=parent.getWidth();int间距=(int)(parentWidth-(mItemSize*mSpanCount))/(mSpan计数+1);outRect.left=间距-列*间距/mSpanCount;outRect.right=(列+1)*间距/mSpanCount;if(位置<mSpanCount){outRect.top=间距;}outRect.bottom=间距;}}

回收视图_item.xml

<线性布局xmlns:android=“http://schemas.android.com/apk/res/android"android:layout_width=“@dimen/recycler_view_item_width”...>...</LinearLayout>

尺寸.xml

<dimen name=“recycler_view_item_width”>60dp

活动

int numberOfColumns=3;mRecyclerView.setLayoutManager(新的网格布局管理器(this,numberOfColumns));mRecyclerView.setAdapter(…);mRecyclerView.addItemDecoration(新的GridSpacingItemDecoration(3,getResources().getDimensionPixelSize(R.dimen.recycler_view_item_width));

在此处输入图像描述 在此处输入图像描述

  • 它会根据屏幕大小工作吗?这意味着它在5英寸屏幕上的显示方式,在其他屏幕大小上看起来也一样吗?
    – 苏尼尔
    评论 2017年12月22日16:21
  • 项目的大小将固定,但项目之间的间距可能不同,您可以看到上面的2幅图像以便于理解
    – 
    评论 2017年12月23日1:01
  • 它们在不同的屏幕尺寸上看起来不同。无论如何,谢谢你
    – 苏尼尔
    评论 2017年12月23日2:48
7

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

类GridSpacingItemDecoration(private val columnCount:Int,@Px preferredSpace:Int、private val includeEdge:Boolean):回收视图。项目装饰(){/***在该算法中,空间应除以3而无余量或项目宽度可以有差异*我们希望它们完全相同*/私有val空间=if(preferredSpace%3==0)preferredSpace else(preferredSpace+(3-preferredSpace%3))override fun getItemOffsets(outRect:Rect,view:view,parent:RecyclerView,state:RecyclerView.state?){val位置=parent.getChildAdapterPosition(视图)if(包括Edge){何时{位置%columnCount==0->{outRect.left=空格outRect.right=空格/3}位置%columnCount==columnCont-1->{outRect.right=空间outRect.left=空格/3}其他->{outRect.left=空格*2/3outRect.right=空间*2/3}}if(位置<columnCount){outRect.top=空格}outRect.bottom=空格}其他{何时{位置%columnCount==0->outRect.right=空格*2/3位置%columnCount==columnCount-1->outRect.left=空格*2/3其他->{outRect.left=空格/3outRect.right=空格/3}}if(位置>=columnCount){outRect.top=空格}}}}
1
  • 如果有人喜欢我使用spanCount=1的GridLayoutManager,我会添加以下行columnCount==1->{outRect.left=space outRect.right=space}
    – 厚重感
    评论 2020年6月12日16:17
7

为儿童使用CardView时,可以通过将app:cardUseCompatAdding设置为true来解决项目之间的空格问题。

要获得更大的利润,请放大项目提升。CardElevation是可选的(使用默认值)。

<androidx.cardview.widget。CardView(卡片视图)xmlns:app=“http://schemas.android.com/apk/res-auto"app:cardUseCompatAdding=“true”app:cardElevation=“2dp”>
0
6

复制了@edwardaa提供的代码,我将其完美地支持RTL:

公共类GridSpacingItemDecoration扩展了RecyclerView。项目装饰{私有int spanCount;私有int间距;private boolean includeEdge;private int headerNum;私有布尔值isRtl=TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault())==ViewCompat。布局_方向_ RTL;public GridSpacingItemDecoration(int spanCount,int spacing,boolean includeEdge,int headerNum){this.spanCount=span计数;this.space=间距;this.includeEdge=包含边缘;this.headerNum=头编号;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){int position=parent.getChildAdapterPosition(视图)-headerNum;//项目位置如果(位置>=0){int column=位置%spanCount;//项目列如果(isRtl){column=spanCount-1列;}if(包括Edge){outRect.left=间距-列*间距/spanCount;//间距-列*((1f/spanCount)*间距)outRect.right=(column+1)*间距/spanCount;//(列+1)*((1f/spanCount)*间距)if(位置<spanCount){//上边缘outRect.top=间距;}outRect.bottom=间距;//项目底部}其他{outRect.left=列*间距/spanCount;//列*((1f/spanCount)*间距)outRect.right=间距-(列+1)*间距/spanCount;//间距-(列+1)*((1f/spanCount)*间距)if(位置>=spanCount){outRect.top=间距;//项目顶部}}}其他{outRect.left=0;outRect.right=0;outRect.top=0;outRect.bottom=0;}}}
1
6
class VerticalGridSpacingDecoration(私有值间距:Int):RecyclerView。项目装饰(){覆盖fun getItemOffsets(outRect:矩形,视图:视图,父级:RecyclerView,state:状态) {val layoutManager=parent.layoutManager为?网格布局管理器if(layoutManager==null ||layoutManager.orientation!=VERTICAL){return super.getItemOffsets(outRect,view,parent,state)}val spanCount=布局管理器。spanCountval位置=parent.getChildAdapterPosition(视图)val列=位置%spanCount带(outRect){left=if(column==0)0 else间距/2right=if(column==spanCount.dec())0 else间距/2顶部=if(位置<spanCount)0其他间距}}}
4

上述答案明确了设置边距处理GridLayoutManager和LinearLayoutManager的方法。

但对于StaggeredGridLayoutManager,Pirdad Sakhizada的回答是:“它可能无法很好地与Staggered GridLayotManager配合使用”。这应该是关于IndexOfSpan的问题。

您可以通过以下方式获得:

私有静态类MyItemDecoration扩展了RecyclerView。项目装饰{@覆盖public void getItemOffsets(Rect outRect,View视图,RecyclerView父级,RecyclerView.State状态){super.getItemOffsets(outRect,视图,父级,状态);int index=((StaggeredGridLayoutManager.LayoutParams)view.getLayoutParams()).getSpanIndex();}}
4
公共类GridSpacingItemDecoration扩展了RecyclerView。项目装饰{私有int spanCount;私有int间距;private boolean includeEdge;public GridSpacingItemDecoration(int spanCount,int spacing,boolean includeEdge){this.spanCount=span计数;this.space=间距;this.includeEdge=包含边缘;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){交错网格布局管理器。LayoutParams params=(StaggeredGridLayoutManager.LayoutParams)视图.getLayoutPartams();int列=params.getSpanIndex();if(包括Edge){outRect.left=间距-列*间距/spanCount;//间距-列*((1f/spanCount)*间距)outRect.right=(列+1)*间距/span计数;//(列+1)*((1f/spanCount)*间距)if(位置<spanCount){//上边缘outRect.top=间距;}outRect.bottom=间距;//项目底部}其他{outRect.left=列*间距/spanCount;//列*((1f/spanCount)*间距)outRect.right=间距-(列+1)*间距/span计数;//间距-(列+1)*((1f/spanCount)*间距)if(位置>=spanCount){outRect.top=间距;//项目顶部}}}}

与爱德华的答案稍有不同,不同之处在于如何确定列,因为在诸如不同高度的项目的情况下,不能简单地通过%来确定列span计数

0
4

yqritc的答案对我来说非常有效。但我使用的是Kotlin,所以这里是等效的。

类ItemOffsetDecoration:回收视图。项目装饰{//添加到填充的数量私有val_itemOffset:Int构造函数(itemOffset:Int){_itemOffset=项目偏移}构造函数(@NonNull context:context,@DimenRes itemOffsetId:Int){_itemOffset=上下文.resources.getDimensionPixelSize(itemOffsetId)}/***将填充应用于[Rect](视图的容器)的所有边*/覆盖fun getItemOffsets(outRect:Rect,view:view,parent:RecyclerView,state:Recycle view.state){super.getItemOffsets(outRect、view、parent、state)outRect.set(_itemOffset、_itemOffset、_itemOffset、_itemOffset)}}

其他的都一样。

4

这是我用Kotlin编写的更灵活的版本,您可以在dp中设置参数。

类ItemDividerGrid(私有值numberOfColumns:Int,私有值rowSpacingDP:Float=0f,私有值columnSpacingDP:Float=0,私有值edgeSpacingVerticalDP:Float=0,私人值edgeSpaceHorizontalDP:Flota=0):ItemDecoration(){覆盖fun getItemOffsets(outRect:Rect,view:view,parent:RecyclerView,state:Recycle view.state){val位置=parent.getChildAdapterPosition(视图)val numberOfRows=(parent.adapter?.itemCount?:-1)/numberOfColumnsval列=位置%numberOfColumnsval行=位置/列数val上下文=view.context///水平when(列){0 -> {outRect.left=将Dp转换为像素(edgeSpaceVerticalDP,上下文)outRect.right=convertDpToPixel(columnSpacingDP/2,上下文)}列数-1->{outRect.left=convertDpToPixel(columnSpacingDP/2,上下文)outRect.right=将Dp转换为像素(edgeSpaceVerticalDP,上下文)}其他->{outRect.left=convertDpToPixel(columnSpacingDP/2,上下文)outRect.right=convertDpToPixel(columnSpacingDP/2,上下文)}}//垂直何时(行){0  -> {outRect.top=将Dp转换为像素(edgeSpaceHorizontalDP,上下文)outRect.bottom=将Dp转换为像素(行间距DP/2,上下文)}行数->{outRect.top=convertDpToPixel(rowSpacingDP/2,上下文)outRect.bottom=将Dp转换为像素(edgeSpaceHorizontalDP,上下文)}其他->{outRect.top=convertDpToPixel(rowSpacingDP/2,上下文)outRect.bottom=将Dp转换为像素(行间距DP/2,上下文)}}}fun convertDpToPixel(dp:Float,context:context?):Int{return if(context!=null){val资源=context.resourcesval metrics=资源.displayMetrics(dp*(metrics.densityDpi.toFloat()/显示度量。DENSITY_DEFAULT)).roundToInt()}其他{val metrics=资源.getSystem().displayMetrics(dp*(metrics.densityDpi.toFloat()/DisplayMetrics)。密度_DEFAULT)).roundToInt()}}}

这是我对的修改空间项目装饰哪一个可以列数顶部、底部、左侧和右侧的间距相等.

公共类SpacesItemDecoration扩展了RecyclerView。项目装饰{私有int空间;私有int mNumCol;公共空间项目装饰(int space,int numCol){this.space=空格;this.mNumCol=numCol;}@覆盖public void getItemOffsets(Rect outRect,查看视图,RecyclerView父级,Recycler视图。州-州){//outRect.right=空间;outRect.bottom=空格;//outRect.left=空格;//Log.d(“ttt”,“项目位置”+parent.getChildLayoutPosition(视图));int position=parent.getChildLayoutPosition(视图);如果(mNumCol<=2){if(位置==0){outRect.left=空格;outRect.right=空格/2;}其他{if((位置%mNumCol)!=0) {outRect.left=空格/2;outRect.right=空间;}其他{outRect.left=空格;outRect.right=空格/2;}}}其他{if(位置==0){outRect.left=空格;outRect.right=空格/2;}其他{if((位置%mNumCol)==0){outRect.left=空格;outRect.right=空格/2;}else if((位置%mNumCol)==(mNumCol-1)){outRect.left=空格/2;outRect.right=空间;}其他{outRect.left=空格/2;outRect.right=空格/2;}}}if(位置<mNumCol){outRect.top=空格;}其他{outRect.top=0;}//仅为第一个项目添加上边距,以避免项目之间出现双重空格/*if(parent.getChildLayoutPosition(view)==0){}其他{outRect.top=0;}*/}}

并在逻辑上使用以下代码。

recyclerView.addItemDecoration(新的SpacesItemDecorsion(spacingInPixels,numCol));

对于那些使用交错布局管理器(例如https://i.sstatic.net/J1gjG.jpg)

recyclerView的方法:

getChildAdapterPosition(视图)getChildLayoutPosition(视图)

有时返回-1作为索引,因此设置itemDecor可能会遇到问题。我的解决方案是覆盖已弃用的ItemDecoration方法:

public void getItemOffsets(Rect outRect,int itemPosition,RecyclerView父级)

而不是新手:

public void getItemOffsets(Rect outRect,View View,RecyclerView父级,State State)

这样地:

recyclerView.addItemDecoration(new recyclerView.ItemDecoration){@覆盖public void getItemOffsets(Rect outRect,int itemPosition,RecyclerView父级){适配器。VH VH=(TheAdapter.VH)回收器View.findViewHolder用于适配器位置(itemPosition);查看itemView=vh.itemView//itemView是viewHolder的基本视图//或者可以使用View itemView=layoutManager.findViewByPosition(itemPosition)代替上面的两行。。。未测试交错网格布局管理器。布局参数itemLayoutParams=(StaggeredGridLayoutManager.LayoutParams)itemView.getLayoutPartams();int spanIndex=itemLayoutParams.getSpanIndex();如果(span索引==0)...其他的...}});

到目前为止似乎对我有用:)

1
  • 回答得很好!适用于所有情况,包括非对称的“常规”GridLayoutManager,其中项之间有一个标题项。谢谢!
    – 希兰85
    评论 2018年11月11日6:51

如果你已经滚动到足够远的地方找到这个答案,我会写一个图书馆用于支持垂直/水平、LTR/RTL、LinearLayout/GridLayout管理器和Edge包含的等间距。它基本上是一个文件,所以您可以将该文件复制粘贴到代码中。

在此处输入图像描述

我试着支持交错网格布局但此布局返回的span索引不可靠。我很高兴听到对此的任何建议。

  • 有趣的图书馆,我会试试看。
    – Xam公司
    评论 2021年8月11日5:10
  • 嗯,我试过你的图书馆。更具体地说,我修改了您的代码,使其仅适用于线性和网格布局管理器的VERTICAL方向,并且效果很好。
    – Xam公司
    评论 2021年8月12日3:57
  • @Xam很高兴你喜欢。 评论 2021年8月12日7:24
2

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

假设您希望X的间距相等(例如8dp)。

  1. 在另一个布局中包装CardView项目

  2. 为外部布局填充X/2(4dp)

  3. 使外部布局背景透明

...

<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=“匹配租金”android:layout_height=“match_parent”></android.support.v7.widget。CardView></LinearLayout>
  1. 给你的回收站视图填充X/2(4dp)

...

<android.support.v7.widget。回收站视图android:layout_width=“匹配租金”android:layout_height=“match_parent”android:padding=“4dp”/>

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

2

这个问题的答案似乎比应该的更复杂。以下是我的看法。

假设网格项之间需要1dp的间距。执行以下操作:

  1. 添加的填充0.5 dp每个项目
  2. 添加的填充-0.5分贝回收视图
  3. 就这样!:)
2
2

对于像我这样想要最好答案的人科特林,在这里:

类GridItemDecoration(val间距:Int,私有值spanCount:Int,private val includeEdge:布尔) :回收站视图。项目装饰(){/***将填充应用于[Rect](视图的容器)的所有边*/覆盖fun getItemOffsets(outRect:矩形,视图:视图,父级:RecyclerView,状态:RecyclerView。) {val position=parent.getChildAdapterPosition(view)//项目位置val列=位置%spanCount//项列if(包括Edge){出口矩形左=spacing-列*间距/spanCount//间距-列*((1f/spanCount)*间距)outRect.右侧=(第+1列)*间距/spanCount//(第+1栏)*((1f/spanCount)*间距)if(位置<spanCount){//上边缘outRect.top=间距}outRect.bottom=间距//项底部}其他{outRect.left(左出矩形)=列*间距/spanCount//column*((1f/spanCount)*间距)outRect.右侧=间距-(列+1)*间距/spanCount//间距-(列+1)*((1f/spanCourt)*间距)if(位置>=spanCount){outRect.top=间距//项顶部}}}}

如果您想从dimens.xml中获取数字,然后将其转换为原始像素,可以使用获取尺寸像素偏移很容易这样:

回收商查看.addItemDecoration(网格项装饰(resources.getDimensionPixelOffset(R.dimen.h1),三,真的))
2

我根据爱德华的伟大回答制作的科特林版本

类RecyclerItemDecoration(私有值span计数:Int,私有值间距:Int):Recycler视图。项目装饰(){覆盖fun getItemOffsets(outRect:Rect,view:view,parent:RecyclerView,state:Recycle view.state){val spacing=数学.round(spacing*parent.context.resources.displayMetrics.density)val位置=parent.getChildAdapterPosition(视图)val列=位置%spanCountoutRect.left=间距-列*间距/span计数outRect.right=(列+1)*间距/spanCountoutRect.top=如果(位置<spanCount)间距为0outRect.bottom=间距}}
1

这将适用于回收站视图也带有页眉。

公共类GridSpacingItemDecoration扩展了RecyclerView。项目装饰{私有int spanCount;私有int间距;private boolean includeEdge;private int headerNum;public GridSpacingItemDecoration(int spanCount,int spacing,boolean includeEdge,int headerNum){this.spanCount=span计数;this.space=间距;this.includeEdge=包含边缘;this.headerNum=头编号;}@覆盖public void getItemOffsets(Rect outRect,View View,RecyclerView父级,Recycle View.State状态){int position=parent.getChildAdapterPosition(视图)-headerNum;//项目位置if(位置>=0){int column=位置%spanCount;//项目列if(包括Edge){outRect.left=间距-列*间距/spanCount;//spacing-列*((1f/spanCount)*间距)outRect.right=(column+1)*间距/spanCount;//(列+1)*((1f/spanCount)*间距)if(位置<spanCount){//上边缘outRect.top=间距;}outRect.bottom=间距;//项目底部}其他{outRect.left=列*间距/span计数;//列*((1f/spanCount)*间距)outRect.right=间距-(列+1)*间距/spanCount;//间距-(列+1)*((1f/spanCount)*间距)if(位置>=spanCount){outRect.top=间距;//项目顶部}}}其他{outRect.left=0;outRect.right=0;outRect.top=0;outRect.bottom=0;}}}}
1
1

对于交错网格布局管理器用户,请小心,这里有很多答案,包括投票最多的一个,用以下代码计算项目列:

int列=位置%spanCount

假设第一/第三/第五/。。项目始终位于左侧和2/4/6/。。项目始终位于右侧。这个假设总是正确的吗?不。

假设你的第一件物品高100dp,第二件只有50dp,猜猜你的第三件物品在哪里,左边还是右边?

1

对我来说,完美的解决方案是将layoutmanager作为GridLayoutManager的RecyclerView的宽度设置为“包装_内容"

1

如果您有一个在列表和网格之间切换的切换开关,不要忘记调用recyclerView.removeItemDecoration(..)在设置任何新项目装饰之前。否则,新的间距计算将不正确。

大致如下:

recyclerView.removeItemDecoration(gridItemDecorator)recyclerView.removeItemDecoration(listItemDecorator)if(showAsList){recyclerView.layoutManager=线性布局管理器(this,LinearLayoutManager.VERTICAL,false)recyclerView.addItemDecoration(listItemDecorator)}其他{recyclerView.layoutManager=网格布局管理器(this,spanCount)recyclerView.addItemDecoration(gridItemDecorator)}
1

确保在分级模块中实现这一点:

实现“com.github.grzegorzojdana:SpacingItemDecoration:1.1.0”

创建此简单函数:

public static int dpToPx(上下文c,int dp){资源r=c.getResources();return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,r.getDisplayMetrics()));}

最后,按照下面这行所示的间距执行:

photosRecycler.addItemDecoration(新的间距项目装饰(2,dpToPx(this,4),true));
0

To制造https://stackoverflow.com/a/29905000/1649371(上图)解决方案工作我不得不修改以下方法(以及所有后续调用)

@禁止警告(“全部”)protected int getItemSpanSize(RecyclerView父级,View视图,int childIndex){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(GridLayoutManager的mgr实例){return((GridLayoutManager)mgr).getSpanSizeLookup().getSpranSize(childIndex);}else if(交错网格布局管理器的mgr实例){return((交错网格布局管理器.LayoutParams)view.getLayoutParams()).isFullSpan()?span计数:1;}else if(线性布局管理器的mgr实例){返回1;}返回-1;}@禁止警告(“全部”)protected int getItemSpanIndex(RecyclerView父级,View视图,int childIndex){回收站视图。LayoutManager管理器=parent.getLayoutManager();if(GridLayoutManager的mgr实例){return((GridLayoutManager)mgr).getSpanSizeLookup().getSpranIndex(childIndex,spanCount);}else if(交错网格布局管理器的mgr实例){return((StaggeredGridLayoutManager.LayoutParams)view.getLayoutParams()).getSpanIndex();}else if(线性布局管理器的mgr实例){返回0;}返回-1;}
0

如果您正在使用标题具有网格布局管理器使用编写的代码科特林对于网格之间的间距:

内部类SpacesItemDecoration(itemSpace:Int):RecyclerView。项目装饰(){var空间:Int=itemSpace覆盖fun getItemOffsets(outRect:Rect?,view:view?,parent:RecyclerView?,state:Recycle view.state?){super.getItemOffsets(outRect、view、parent、state)val位置=父级!!。获取儿童适配器位置(视图)val viewType=parent.adapter.getItemViewType(位置)//选中可不对标题项设置任何边距if(viewType==GridViewAdapter.TYPE_HEADER){出矩形!!。顶部=0outRect.left=0outRect.right=0outRect.bottom=0}其他{出矩形!!。left=空格outRect.right=空间outRect.bottom=空格if(parent.getChildLayoutPosition(view)==0){outRect.top=空格}其他{outRect.top=0}}}}

然后通过项目装饰回收站视图作为:

gridView.addItemDecoration(空间项目装饰(10))
1

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.