Decorating RecyclerView (with GridLayoutManager) to display divider between items

后端 未结 9 1441
旧时难觅i
旧时难觅i 2020-12-07 09:44

What\'s the best and easiest way to decorate RecyclerView to have such look & feel?

\"enter

相关标签:
9条回答
  • 2020-12-07 10:06
      @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;
            }
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-07 10:07

    Here's my implementation in Kotlin. I modified code from other answers so that divider is shown also for full spanned items.

    import android.graphics.Rect
    import android.view.View
    import androidx.recyclerview.widget.GridLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    
    class GridDividerItemDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {
    
        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            val position = parent.getChildAdapterPosition(view)
    
            val totalSpanCount = getTotalSpanCount(parent)
            val spanSize = getItemSpanSize(parent, position)
    
            outRect.top = if (isInTheFirstRow(position, totalSpanCount)) 0 else spacing
            outRect.left = if (isFirstInRow(position, totalSpanCount, spanSize)) 0 else spacing / 2
            outRect.right = if (isLastInRow(position, totalSpanCount, spanSize)) 0 else spacing / 2
            outRect.bottom = 0
        }
    
        private fun isInTheFirstRow(position: Int, totalSpanCount: Int): Boolean =
            position < totalSpanCount
    
        private fun isFirstInRow(position: Int, totalSpanCount: Int, spanSize: Int): Boolean =
            if (totalSpanCount != spanSize) {
                position % totalSpanCount == 0
            } else true
    
        private fun isLastInRow(position: Int, totalSpanCount: Int, spanSize: Int): Boolean =
            isFirstInRow(position + 1, totalSpanCount, spanSize)
    
        private fun getTotalSpanCount(parent: RecyclerView): Int =
            (parent.layoutManager as? GridLayoutManager)?.spanCount ?: 1
    
        private fun getItemSpanSize(parent: RecyclerView, position: Int): Int =
            (parent.layoutManager as? GridLayoutManager)?.spanSizeLookup?.getSpanSize(position) ?: 1
    }
    

    Result:

    0 讨论(0)
  • 2020-12-07 10:15

    You may got your answer but i'm still posting my solution that can help others. This can be used for vertical, horizontal lists or grid views by passing the orientation.

    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());
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题