如何使用GridLayoutManager与RecyclerView设置列间距? 在我的布局中设置空白/填充没有效果。
当前回答
这也适用于RecyclerView with header。
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;
}
}
}
}
其他回答
这就是最后对我有用的方法:
binding.rows.addItemDecoration(object: RecyclerView.ItemDecoration() {
val px = resources.getDimensionPixelSize(R.dimen.grid_spacing)
val spanCount = 2
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val index = parent.getChildLayoutPosition(view)
val isLeft = (index % spanCount == 0)
outRect.set(
if (isLeft) px else px/2,
0,
if (isLeft) px/2 else px,
px
)
}
})
因为我只有2列(val spanCount = 2),我可以只做isLeft。如果有> 2列,那么我也需要一个isMiddle,两边的值都是px/2。
我希望有一种方法来获得应用程序:spanCount从直接从RecyclerView,但我不相信有。
下面是一个不需要“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));
这是我在Kotlin中编写的更灵活的版本,您可以在dp中设置参数。
class ItemDividerGrid(private val numberOfColumns: Int, private val rowSpacingDP: Float = 0f, private val columnSpacingDP: Float = 0f, private val edgeSpacingVerticalDP: Float = 0f, private val edgeSpacingHorizontalDP: Float = 0f) : ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val position = parent.getChildAdapterPosition(view)
val numberOfRows = (parent.adapter?.itemCount?:-1)/numberOfColumns
val column = position % numberOfColumns
val row = position / numberOfColumns
val context = view.context
///horizontal
when(column){
0 -> {
outRect.left = convertDpToPixel(edgeSpacingVerticalDP,context)
outRect.right = convertDpToPixel(columnSpacingDP/2, context)
}
numberOfColumns-1 -> {
outRect.left = convertDpToPixel(columnSpacingDP/2, context)
outRect.right = convertDpToPixel(edgeSpacingVerticalDP, context)
}
else -> {
outRect.left = convertDpToPixel(columnSpacingDP/2, context)
outRect.right = convertDpToPixel(columnSpacingDP/2, context)
}
}
//vertical
when(row){
0 -> {
outRect.top = convertDpToPixel(edgeSpacingHorizontalDP,context)
outRect.bottom = convertDpToPixel(rowSpacingDP/2, context)
}
numberOfRows -> {
outRect.top = convertDpToPixel(rowSpacingDP/2, context)
outRect.bottom = convertDpToPixel(edgeSpacingHorizontalDP, context)
}
else -> {
outRect.top = convertDpToPixel(rowSpacingDP/2, context)
outRect.bottom = convertDpToPixel(rowSpacingDP/2, context)
}
}
}
fun convertDpToPixel(dp: Float, context: Context?): Int {
return if (context != null) {
val resources = context.resources
val metrics = resources.displayMetrics
(dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).roundToInt()
} else {
val metrics = Resources.getSystem().displayMetrics
(dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).roundToInt()
}
}
}
对于这个问题,有一个非常简单而灵活的解决方案,它只使用适用于每个LayoutManager的XML。
假设你想要一个相等的X间距(例如8dp)。
在另一个布局中包装CardView项 给外层布局填充X/2 (4dp) 使外层布局背景透明
...
<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="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.CardView>
</LinearLayout>
给你的RecyclerView填充X/2 (4dp)
...
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp" />
就是这样。你有完美的X (8dp)间距。
这是我对SpacesItemDecoration的修改,它可以在顶部,底部,左边和右边同样采取numofcolumns和空间。
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<mNumCol){
outRect.top=space;
}else{
outRect.top=0;
}
// Add top margin only for the first item to avoid double space between items
/*
if (parent.getChildLayoutPosition(view) == 0 ) {
} else {
outRect.top = 0;
}*/
}
}
并在逻辑上使用下面的代码。
recyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels, numCol));
推荐文章
- 警告:API ' variable . getjavacompile()'已过时,已被' variable . getjavacompileprovider()'取代
- 安装APK时出现错误
- 碎片中的onCreateOptionsMenu
- TextView粗体通过XML文件?
- 如何使线性布局的孩子之间的空间?
- DSL元素android.dataBinding。enabled'已过时,已被'android.buildFeatures.dataBinding'取代
- ConstraintLayout:以编程方式更改约束
- PANIC: AVD系统路径损坏。检查ANDROID_SDK_ROOT值
- 如何生成字符串类型的buildConfigField
- Recyclerview不调用onCreateViewHolder
- Android API 21工具栏填充
- Android L中不支持操作栏导航模式
- 如何在TextView中添加一个子弹符号?
- PreferenceManager getDefaultSharedPreferences在Android Q中已弃用
- 在Android Studio中创建aar文件