Psest328
Psest328

Reputation: 6675

RecyclerView ItemDecoration: Spacing AND Divider

Below is how I'm doing the spacing for RecyclerView items. It's designed to work with both grids and lists. The spacing works.

What I can't figure out is how to insert a divider line as well. Any help doing so would be greatly appreciated.

SIDE NOTE: if you have a better way to implement the spacing than what I'm currently doing, I'd be very grateful as well :)

public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {

    private int numOfColumns;
    private int listSize;
    private int offsetInDp;
    private boolean isGridView;
    private boolean canScrollHorizontally;
    private boolean isBottomRow = false;


    public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int listSize, int offsetInDp) {
        this(manager, 1, listSize, offsetInDp);
    }

    public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int numOfColumns, int listSize, int offsetInDp) {
        this.numOfColumns = numOfColumns;
        this.listSize = listSize;
        this.offsetInDp = PixelConversionUtils.dpToPx(offsetInDp);

        this.isGridView = manager instanceof GridLayoutManager;
        this.canScrollHorizontally = manager.canScrollHorizontally();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {

        // only do left/right spacing if grid or horizontal list
        if (isGridView || canScrollHorizontally) {
            outRect.left = offsetInDp;
            outRect.right = offsetInDp;
        }

        // only do top/bottom spacing if grid or vertical list
        if (isGridView || !canScrollHorizontally) {
            int pos = parent.getChildAdapterPosition(view);
            boolean isNotTopRow = pos >= numOfColumns;

            // Don't add top spacing to top row
            if (isNotTopRow) {
                outRect.top = offsetInDp;
            }

            int columnIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();

            if (pos >= (listSize - numOfColumns) && columnIndex == 0) {
                isBottomRow = true;
            }

            // Don't add bottom spacing to bottom row
            if (!isBottomRow && pos < (listSize - numOfColumns)) {
                outRect.bottom = offsetInDp;
            }
        }
    }
}

here's a quick visual of what I'm looking to do:

here's what I have:

enter image description here

here's what I want:

enter image description here

Upvotes: 0

Views: 1610

Answers (3)

Artem
Artem

Reputation: 528

You can achieve desired look this way:

  • first, create a divider Drawable, for this example I've used a simple shape, but you could use default line divider or any other drawable:

    <shape xmlns:android="http://schemas.android.com/apk/res/android" 
        android:shape="rectangle">
        <size android:height="2dp" />
        <size android:width="2dp" />
        <solid android:color="#000000" />
    </shape>
    
  • second, in your ItemOffsetDecoration declare Drawable and initialize it:

    public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
    
        private Drawable mDivider;
    
        ...
    
        public ItemOffsetDecoration(...) {
            mDivider = ContextCompat.getDrawable(context, R.drawable.item_divider);
        }
    }
    
  • third, override onDrawOver() method:

    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (isGridView) {
            drawVerticalDivider(c, parent);
        } else {
            drawVerticalDivider(c, parent);
            drawHorizontalDivider(c, parent);
        }
    
    }
    

    where drawVerticalDivider() & drawHorizontalDivider() are (might be a good idea to refactor them into the single method and control direction of the divider via parameter):

    public void drawVerticalDivider(Canvas c, RecyclerView parent) {
        if (parent.getChildCount() == 0) return;
    
        final int childCount = parent.getChildCount();
    
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params =
                    (RecyclerView.LayoutParams) child.getLayoutParams();
    
            int left = child.getLeft() - params.leftMargin - offsetInDp;
            int right = child.getRight() + params.rightMargin + offsetInDp;
            int top = child.getBottom() + params.bottomMargin + offsetInDp;
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    
    public void drawHorizontalDivider(Canvas c, RecyclerView parent) {
        final int childCount = parent.getChildCount();
    
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params =
                    (RecyclerView.LayoutParams) child.getLayoutParams();
    
            int left = child.getRight() + params.rightMargin + offsetInDp;
            int right = left + mDivider.getIntrinsicWidth();
            int top = child.getTop() - params.topMargin - offsetInDp;
            int bottom = child.getBottom() + params.bottomMargin + offsetInDp;
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    

Result for the Linear and Grid LayoutManagers:

LinearLayoutManager GridLayoutManager

Upvotes: 3

Cgx
Cgx

Reputation: 763

haha……actualy,i had try like this for Divider ,although with a bit funny : first make you recycleview backgroud with Deep color,and make item_view backgroud white,then marginbottom for every item -> I'm serious, do not vote down :)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_marginBottom="2dp"
    android:layout_height="wrap_content">

    <TextView
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

</LinearLayout>

Upvotes: 0

not_again_stackoverflow
not_again_stackoverflow

Reputation: 1323

Try placing the following XML snippet to get a divider:

<View android:layout_width="match_parent"
      android:layout_height="1dp"
      android:layout_marginLeft="72dp"
      android:layout_marginRight="16dp"
      android:background="123e4152"/>

You can put this in the recyclerView's item layout beneath your items. Also Play around with the margins and background to suit your list.

Upvotes: 0

Related Questions