装饰RecyclerView(使用GridLayoutManager)显示项目之间的分隔符

装饰RecyclerView具有这种外观和感觉的最好和最简单的方法是什么?

在这里输入图像说明

这里的主要挑战是在物品之间只有分隔物,而不是在物品和屏幕的左/右边界之间。

有任何想法吗?

我不知道为什么你需要这个,但是这个UI很容易用RecyclerView装饰器来实现。

mRecylerView.addItemDecoration(new ItemDecorationAlbumColumns( getResources().getDimensionPixelSize(R.dimen.photos_list_spacing), getResources().getInteger(R.integer.photo_list_preview_columns))); 

和装饰(需要一些refatoring)

 import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.view.View; public class ItemDecorationAlbumColumns extends RecyclerView.ItemDecoration { private int mSizeGridSpacingPx; private int mGridSize; private boolean mNeedLeftSpacing = false; public ItemDecorationAlbumColumns(int gridSpacingPx, int gridSize) { mSizeGridSpacingPx = gridSpacingPx; mGridSize = gridSize; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int frameWidth = (int) ((parent.getWidth() - (float) mSizeGridSpacingPx * (mGridSize - 1)) / mGridSize); int padding = parent.getWidth() / mGridSize - frameWidth; int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition(); if (itemPosition < mGridSize) { outRect.top = 0; } else { outRect.top = mSizeGridSpacingPx; } if (itemPosition % mGridSize == 0) { outRect.left = 0; outRect.right = padding; mNeedLeftSpacing = true; } else if ((itemPosition + 1) % mGridSize == 0) { mNeedLeftSpacing = false; outRect.right = 0; outRect.left = padding; } else if (mNeedLeftSpacing) { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx - padding; if ((itemPosition + 2) % mGridSize == 0) { outRect.right = mSizeGridSpacingPx - padding; } else { outRect.right = mSizeGridSpacingPx / 2; } } else if ((itemPosition + 2) % mGridSize == 0) { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx / 2; outRect.right = mSizeGridSpacingPx - padding; } else { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx / 2; outRect.right = mSizeGridSpacingPx / 2; } outRect.bottom = 0; } } 

在这里输入图像说明在这里输入图像说明

你可能会得到你的答案,但我仍然发布我的解决scheme,可以帮助别人。 这可以通过传递方向用于垂直,水平列表或网格视图。

 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; public static final int GRID = 2; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST && orientation != GRID) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else if(mOrientation == HORIZONTAL_LIST){ drawHorizontal(c, parent); } else { drawVertical(c, parent); drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { if (parent.getChildCount() == 0) return; final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final View child = parent.getChildAt(0); if (child.getHeight() == 0) return; final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int top = child.getBottom() + params.bottomMargin + mDivider.getIntrinsicHeight(); int bottom = top + mDivider.getIntrinsicHeight(); final int parentBottom = parent.getHeight() - parent.getPaddingBottom(); while (bottom < parentBottom) { mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); top += mDivider.getIntrinsicHeight() + params.topMargin + child.getHeight() + params.bottomMargin + mDivider.getIntrinsicHeight(); bottom = top + mDivider.getIntrinsicHeight(); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int left = child.getRight() + params.rightMargin + mDivider.getIntrinsicHeight(); final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else if(mOrientation == HORIZONTAL_LIST) { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } } 
  @BindingAdapter({"bind:adapter"}) public static void bind(RecyclerView view, RecyclerView.Adapter<BaseViewHolder> adapter) { view.setLayoutManager(new GridLayoutManager(view.getContext(), 3)); view.addItemDecoration(new SpacesItemDecorationGrid(view.getContext(), 4, 3)); view.setItemAnimator(new DefaultItemAnimator()); view.setAdapter(adapter); } public class SpacesItemDecorationGrid extends RecyclerView.ItemDecoration { private int mSizeGridSpacingPx; private int mGridSize; private boolean mNeedLeftSpacing = false; /** * @param gridSpacingPx * @param gridSize */ SpacesItemDecorationGrid(Context context, int gridSpacingPx, int gridSize) { mSizeGridSpacingPx = (int) Util.convertDpToPixel(gridSpacingPx, context); mGridSize = gridSize; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int frameWidth = (int) ((parent.getWidth() - (float) mSizeGridSpacingPx * (mGridSize - 1)) / mGridSize); int padding = parent.getWidth() / mGridSize - frameWidth; int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition(); int itemCount = parent.getAdapter().getItemCount() - mGridSize; /* if (itemPosition < mGridSize) { outRect.top = mSizeGridSpacingPx; } else { outRect.top = mSizeGridSpacingPx; }*/ outRect.top = mSizeGridSpacingPx; if (itemPosition % mGridSize == 0) { outRect.left = mSizeGridSpacingPx; outRect.right = padding; mNeedLeftSpacing = true; } else if ((itemPosition + 1) % mGridSize == 0) { mNeedLeftSpacing = false; outRect.right = mSizeGridSpacingPx; outRect.left = padding; } else if (mNeedLeftSpacing) { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx - padding; if ((itemPosition + 2) % mGridSize == 0) { outRect.right = mSizeGridSpacingPx - padding; } else { outRect.right = mSizeGridSpacingPx / 2; } } else if ((itemPosition + 2) % mGridSize == 0) { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx / 2; outRect.right = mSizeGridSpacingPx - padding; } else { mNeedLeftSpacing = false; outRect.left = mSizeGridSpacingPx / 2; outRect.right = mSizeGridSpacingPx / 2; } if (itemPosition > itemCount) { outRect.bottom = mSizeGridSpacingPx; } else { outRect.bottom = 0; } } } 

一个更简单的解决scheme,为我工作。 希望它可以是有用的。

 class GridItemDecorator(val context: Context, private val spacingDp: Int, private val mGridSize: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val resources = context.resources val spacingPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, spacingDp.toFloat(), resources.displayMetrics) val bit = if (spacingPx > mGridSize) Math.round(spacingPx / mGridSize) else 1 val itemPosition = (view.layoutParams as RecyclerView.LayoutParams).viewAdapterPosition outRect.top = if (itemPosition < mGridSize) 0 else bit * mGridSize outRect.bottom = 0 val rowPosition = itemPosition % mGridSize outRect.left = rowPosition * bit outRect.right = (mGridSize - rowPosition - 1) * bit } } 

这是一个更简单,用户友好的实现:

 public class MediaSpaceDecoration extends RecyclerView.ItemDecoration { private final int spacing; private final List<Integer> allowedViewTypes = Arrays.asList( R.layout.item_image, R.layout.item_blur); public MediaSpaceDecoration(int spacing) { this.spacing = spacing; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { final int position = parent.getChildAdapterPosition(view); if (!isMedia(parent, position)) { return; } final int totalSpanCount = getTotalSpanCount(parent); int spanSize = getItemSpanSize(parent, position); if (totalSpanCount == spanSize) { return; } outRect.top = isInTheFirstRow(position, totalSpanCount) ? 0 : spacing; outRect.left = isFirstInRow(position, totalSpanCount) ? 0 : spacing / 2; outRect.right = isLastInRow(position, totalSpanCount) ? 0 : spacing / 2; outRect.bottom = 0; // don't need } private boolean isInTheFirstRow(int position, int spanCount) { return position < spanCount; } private boolean isFirstInRow(int position, int spanCount) { return position % spanCount == 0; } private boolean isLastInRow(int position, int spanCount) { return isFirstInRow(position + 1, spanCount); } private int getTotalSpanCount(RecyclerView parent) { final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); return layoutManager instanceof GridLayoutManager ? ((GridLayoutManager) layoutManager).getSpanCount() : 1; } private int getItemSpanSize(RecyclerView parent, int position) { final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); return layoutManager instanceof GridLayoutManager ? ((GridLayoutManager) layoutManager).getSpanSizeLookup().getSpanSize(position) : 1; } private boolean isMedia(RecyclerView parent, int viewPosition) { final RecyclerView.Adapter adapter = parent.getAdapter(); final int viewType = adapter.getItemViewType(viewPosition); return allowedViewTypes.contains(viewType); } } 

我也检查设置outRect之前,因为我有各种各样的spanSize s,我需要添加一个额外的中间空间只允许allowedViewTypes 。 您可以轻松删除该validation,代码会更简单。 这对我来说是这样的: GridLayout为每个viewType使用不同的spanSize