Boycott A.I.
Boycott A.I.

Reputation: 18931

Android - How to add a dashed/dotted separator line for RecyclerView?

I have used the code from this answer to create a solid separator line for my RecyclerViews.

However, I would like the line to be dashed/dotted.

I already have a line_dashed.xml resource that I am using elsewhere in my app:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="line" >

    <stroke
        android:color="@color/blue"
        android:dashGap="12dp"
        android:dashWidth="12dp"
        android:width="1dp" />

</shape>

But if I try applying this as the drawable resource that is accessed via my call to recyclerView.addItemDecoration(new SimpleDividerItemDecoration(getContext())), no line is drawn at all.

How to solve so a dashed line is shown?

Upvotes: 8

Views: 5242

Answers (3)

Alex
Alex

Reputation: 148

currently u can use DividerItemDecoration from the box.

recyclerView.apply {
        layoutManager = LinearLayoutManager([email protected])
        adapter = [email protected]
        addItemDecoration(
            DividerItemDecoration(
                [email protected],
                DividerItemDecoration.VERTICAL
            ).apply {
                context.getDrawable(R.drawable.divider)?.let {
                    setDrawable(it)
                }
            }
        )
    }

Use the next shape XML:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">

    <size android:height="1dp" />

    <solid android:color="@color/primary" />

    <stroke
        android:width="0.5dp"
        android:color="@color/primary"
        android:dashWidth="5dp"
        android:dashGap="5dp" />
</shape>

Attention: The stroke width must be less than the height of the line. In another case, the line will be not drawn.

Upvotes: 2

Mykhailo
Mykhailo

Reputation: 361

Just add your drawable resource into this item decorator.

DividerItemDecoration decorator = new DividerItemDecoration(ContextCompat.getDrawable(getContext(), R.drawable.line_dashed));
recyclerView.addItemDecoration(decorator);

and DividerItemDecorator class:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private Drawable mDivider;
    private int mPaddingLeft;

    public DividerItemDecoration(Drawable divider) {
        mDivider = divider;
        mPaddingLeft = 0;
    }

    public DividerItemDecoration(Drawable divider, int paddingLeft) {
        mDivider = divider;
        mPaddingLeft = paddingLeft;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (mDivider == null) return;
        if (parent.getChildAdapterPosition(view) < 1) return;

        if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
            outRect.top = mDivider.getIntrinsicHeight();
        } else {
            outRect.left = mDivider.getIntrinsicWidth();
        }
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mDivider == null) {
            super.onDrawOver(c, parent, state);
            return;
        }

        if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
            final int left = parent.getPaddingLeft() + mPaddingLeft;
            final int right = parent.getWidth() - parent.getPaddingRight();
            final int childCount = parent.getChildCount();

            for (int i = 1; i < childCount; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                final int size = mDivider.getIntrinsicHeight();
                final int top = child.getTop() - params.topMargin;
                final int bottom = top + size;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }

        } else { //horizontal
            final int top = parent.getPaddingTop();
            final int bottom = parent.getHeight() - parent.getPaddingBottom();
            final int childCount = parent.getChildCount();

            for (int i = 1; i < childCount; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                final int size = mDivider.getIntrinsicWidth();
                final int left = child.getLeft() - params.leftMargin;
                final int right = left + size;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    }

    private int getOrientation(RecyclerView parent) {
        if (parent.getLayoutManager() instanceof LinearLayoutManager) {
            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
            return layoutManager.getOrientation();
        } else
            throw new IllegalStateException("DividerItemDecoration can only be used with a LinearLayoutManager.");
    }
}

Should work I tested it.

UPDATE:

    android:layerType="software"

add this parameter in xml for recyclerView Also add size into your shape drawable:

<size android:height="1dp"/>

Upvotes: 6

Roman_D
Roman_D

Reputation: 4740

Draw dashed line in Android is not so easy deal. Drowable like you showed and even just draw dotted line on canwas (canvas.drawLine(..., paintWithDashEffect)) not always works (not for all devices). You may use android:layerType="software" or draw path. IMHO, the better solution is to not draw dotted line at all (draw just line). But if you really need dotted line, you can use @fearless answer or somethink like this:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private Paint mPaint;
private int mDividerSize;

public DividerItemDecoration(int dividerSize) {
    mDividerSize = dividerSize;
    mPaint = new Paint();
    mPaint.setColor(ContextCompat.getColor(context, R.color.colorAccent));
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(dividerSize);
    mPaint.setPathEffect(new DashPathEffect(new float[]{dashGap,dashWidth},0));
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    outRect.bottom = mDividerSize;
}

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    int left = parent.getPaddingLeft();
    int right = parent.getWidth() - parent.getPaddingRight();

    int childCount = parent.getChildCount();
    Path path = new Path();
    for (int i = 0; i < childCount; i++) {
        View child = parent.getChildAt(i);

        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

        int top = child.getBottom() + params.bottomMargin + mDividerSize/2;

        path.moveTo(left, top);
        path.lineTo(right, top);
    }
    c.drawPath(path, mPaint);
}

}

Upvotes: 0

Related Questions