如何使用GridLayoutManager与RecyclerView设置列间距? 在我的布局中设置空白/填充没有效果。
当前回答
下面的代码工作得很好,每个列都有相同的宽度:
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));
其他回答
如果你使用Header和GridLayoutManager,使用以下代码在Kotlin中编写的网格间距:
inner class SpacesItemDecoration(itemSpace: Int) : RecyclerView.ItemDecoration() {
var space: Int = itemSpace
override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent!!.getChildAdapterPosition(view)
val viewType = parent.adapter.getItemViewType(position)
// Check to not to set any margin to header item
if (viewType == GridViewAdapter.TYPE_HEADER) {
outRect!!.top = 0
outRect.left = 0
outRect.right = 0
outRect.bottom = 0
} else {
outRect!!.left = space
outRect.right = space
outRect.bottom = space
if (parent.getChildLayoutPosition(view) == 0) {
outRect.top = space
} else {
outRect.top = 0
}
}
}
}
并将ItemDecoration传递给RecyclerView为:
gridView.addItemDecoration(SpacesItemDecoration(10))
试试这个。它会照顾到周围的等距。适用于列表,网格和StaggeredGrid。
编辑
更新后的代码应该可以处理大多数带有跨度、方向等的角落情况。 注意,如果在GridLayoutManager中使用setSpanSizeLookup(),出于性能考虑,建议设置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);
}
}
希望能有所帮助。
下面的代码工作得很好,每个列都有相同的宽度:
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));
如果你有一个在列表和网格之间切换的拨动开关,不要忘记在设置任何新的项目装饰之前调用recyclerView.removeItemDecoration(..)。如果不是这样,那么新的间隔计算将是不正确的。
就像这样:
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)
}
如果你已经滚动到这个答案,我写了一个等间距库,支持垂直/水平,LTR/RTL,线性布局/GridLayout管理器和边缘包含。它基本上是一个文件,所以你可以复制粘贴该文件到你的代码中。
我试图支持StaggeredGridLayout,但这个布局返回的跨度索引不可靠。我很乐意听到任何有关这方面的建议。
推荐文章
- BottomSheetDialogFragment的圆角
- 在应用程序启动时出现“无法获得BatchedBridge,请确保您的bundle被正确打包”的错误
- 我如何改变默认对话框按钮的文本颜色在安卓5
- 更改单选按钮的圆圈颜色
- 如何在android中复制一个文件?
- adb找不到我的设备/手机(MacOS X)
- 如何在新的材质主题中改变背面箭头的颜色?
- androidviewpager与底部点
- 相同的导航抽屉在不同的活动
- 如何从视图中获得托管活动?
- 单一的TextView与多种颜色的文本
- 如何在非活动类(LocationManager)中使用getSystemService ?
- 在清单中注册应用程序类?
- Android:从数组中编程创建旋转器
- Android命令行工具sdkmanager总是显示:警告:无法创建设置