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 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);
}
}
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
}
}
}
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
而不是新手:
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
像这样:
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
TheAdapter.VH vh = (TheAdapter.VH) recyclerView.findViewHolderForAdapterPosition(itemPosition);
View itemView = vh.itemView; //itemView is the base view of viewHolder
//or instead of the 2 lines above maybe it's possible to use View itemView = layoutManager.findViewByPosition(itemPosition) ... NOT TESTED
StaggeredGridLayoutManager.LayoutParams itemLayoutParams = (StaggeredGridLayoutManager.LayoutParams) itemView.getLayoutParams();
int spanIndex = itemLayoutParams.getSpanIndex();
if (spanIndex == 0)
...
else
...
}
});
到目前为止似乎对我有用:)
5
我最终为我的 RecyclerView with GridLayoutManager and HeaderView 做了这样的事情 .
23 回答
RecyclerViews支持ItemDecoration的概念:特殊偏移和绘制每个元素 . 如this answer中所示,您可以使用
然后通过添加它
复制的@edwardaa提供了代码,我完全支持RTL:
以下代码运行良好,每列具有相同的宽度:
用法
1.没有优势
2.有边缘
如果您希望项目周围的间距相等且项目大小相等,则以下是逐步简单的解决方案 .
ItemOffsetDecoration
实施
在源代码中,将
ItemOffsetDecoration
添加到RecyclerView.
项目偏移值应该是要添加为项目之间空间的实际值的一半 .另外,将项目偏移值设置为
RecyclerView
的填充,并指定android:clipToPadding=false
.试试这个 . 它会照顾到周围相等的间距 . 适用于List,Grid和StaggeredGrid .
已编辑
更新的代码应该处理大多数具有 Span ,方向等的极端情况 . 请注意,如果将setSpanSizeLookup()与GridLayoutManager一起使用,则出于性能原因,建议设置setSpanIndexCacheEnabled() .
注意,似乎使用StaggeredGrid,似乎存在一个错误,其中子项的索引变得古怪且难以跟踪,因此下面的代码可能无法很好地使用StaggeredGridLayoutManager .
希望能帮助到你 .
以下代码将处理StaggeredGridLayoutManager,GridLayoutManager和LinearLayoutManager .
然后使用它
这是我使用它的 doesn't require "spanCount" (列数)的解决方案,因为我使用 GridAutofitLayoutManager (根据所需的单元格大小计算列数)
(请注意,这只适用于 GridLayoutManager )
这是 GridAutofitLayoutManager 是有兴趣的人:
Finally:
如果你想 FIXED 所有设备中
RecyclerView
项目的大小 . 你可以这样做recyclerview_item.xml
dimens.xml
Activity
只有一个简单的解决方案,您可以在任何需要的地方记住并实施 . 没有错误,没有疯狂的计算 . 将保证金放入卡/项目布局并将与填充相同的大小放入RecyclerView:
item_layout.xml
activity_layout.xml
上面的答案阐明了设置边距处理GridLayoutManager和LinearLayoutManager的方法 .
但是对于StaggeredGridLayoutManager,Pirdad Sakhizada的回答说:“它可能不适用于StaggeredGridLayoutManager” . 这应该是关于IndexOfSpan的问题 .
你可以通过这种方式得到它:
对于这个问题,有一个非常简单而灵活的解决方案,只使用适用于每个LayoutManager的XML .
假设你想要一个相等的X间距(例如8dp) .
将CardView项目换行到另一个布局中
给外部布局填充X / 2(4dp)
使外部布局背景透明
...
...
就是这样 . 你有完美的X(8dp)间距 .
与edwardaa的答案略有不同,区别在于如何确定列,因为在诸如具有不同高度的项目的情况下,列不能仅通过%
spanCount
来确定这是我对
SpacesItemDecoration
的修改,可以采用 numOfColums 和 space equally on top, bottom, left and right .并在你的逻辑上使用下面的代码 .
这个Link适合我所有情况你可以试试这个 .
我根据edwardaa的精彩答案制作的Kotlin版本
选择的答案几乎是完美的,但根据空间,项目宽度可能不相等 . (就我而言,这很关键) . 所以我最终得到了这个代码,增加了一点空间,所以项目的宽度都相同 .
}
对于那些有staggeredLayoutManager(如https://imgur.com/XVutH5u)问题的人
recyclerView的方法:
有时返回-1作为索引,所以我们可能会遇到设置itemDecor的麻烦 . 我的解决方案是覆盖已弃用的ItemDecoration方法:
而不是新手:
像这样:
到目前为止似乎对我有用:)
我最终为我的 RecyclerView with GridLayoutManager and HeaderView 做了这样的事情 .
在下面的代码中,我在每个项目之间设置了4dp空间(每个项目周围2dp,整个recyclerview周围2dp填充) .
layout.xml
片段/活动
SpaceItemDecoration.java
Utils.java
谢谢edwardaa的回答https://stackoverflow.com/a/30701422/2227031
另一点需要注意的是:
如果总间距和总itemWidth不等于屏幕宽度,则还需要调整itemWidth,例如,在适配器onBindViewHolder方法上
制作https://stackoverflow.com/a/29905000/1649371(上面)解决方案工作我不得不修改以下方法(以及所有后续调用)
如果您有一个切换开关在列表到网格之间切换,请不要忘记在设置任何新项目装饰之前调用
recyclerView.removeItemDecoration()
. 如果没有,那么间距的新计算将是不正确的 .像这样的东西 .
这也适用于带有 Headers 的
RecyclerView
.