How to add dividers and spaces between items in RecyclerView
Asked Answered
S

45

1124

This is an example of how it could have been done previously in the ListView class, using the divider and dividerHeight parameters:

<ListView
    android:id="@+id/activity_home_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@android:color/transparent"
    android:dividerHeight="8dp"/>

However, I don't see such possibility in the RecyclerView class.

<android.support.v7.widget.RecyclerView
    android:id="@+id/activity_home_recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"/>

In that case, is it ok to define margins and/or add a custom divider view directly into a list item's layout or is there a better way to achieve my goal?

Selfcongratulation answered 7/7, 2014 at 20:1 Comment(5)
This helped me: #26892796Pulpwood
@Selfcongratulation Add items <TextView /> another xml and use it in list Same Activity.Compelling
There is a class in support lib com.homeretailgroup.argos.android.view.decorators.DividerItemDecoration and use it like that: mRecyclerView.addItemDecoration(new DividerItemDecoration(activity, LinearLayoutManager.VERTICAL));Vail
The simplest way is to add top/bottom margins around the first item in the adapter's row. android:layout_marginBottom="4dp". (Note adding the margins to the parent layout won't cut it.)Blesbok
Quick and dirty : add line view at top of recycler item view layout. recommended is using divider item decoratorDomiciliate
S
1402

October 2016 Update

The version 25.0.0 of Android Support Library introduced the DividerItemDecoration class:

DividerItemDecoration is a RecyclerView.ItemDecoration that can be used as a divider between items of a LinearLayoutManager. It supports both HORIZONTAL and VERTICAL orientations.

Usage:

DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
    layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);

Previous answer

Some answers either use methods that have since become deprecated, or don't give a complete solution, so I tried to do a short, up-to-date wrap-up.


Unlike ListView, the RecyclerView class doesn't have any divider-related parameters. Instead, you need to extend ItemDecoration, a RecyclerView's inner class:

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter's data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

All ItemDecorations are drawn in the order they were added, before the item views (in onDraw()) and after the items (in onDrawOver(Canvas, RecyclerView, RecyclerView.State).

Vertical spacing ItemDecoration

Extend ItemDecoration, add a custom constructor which takes space height as a parameter and override the getItemOffsets() method:

public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {

    private final int verticalSpaceHeight;

    public VerticalSpaceItemDecoration(int verticalSpaceHeight) {
        this.verticalSpaceHeight = verticalSpaceHeight;
    }

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

If you don't want to insert space below the last item, add the following condition:

if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = verticalSpaceHeight;
}

Note: you can also modify outRect.top, outRect.left and outRect.right properties for the desired effect.

Divider ItemDecoration

Extend ItemDecoration and override the onDraw() method:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable divider;

    /**
     * Default divider will be used
     */
    public DividerItemDecoration(Context context) {
        final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
        divider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    /**
     * Custom divider will be used
     */
    public DividerItemDecoration(Context context, int resId) {
        divider = ContextCompat.getDrawable(context, resId);
    }

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

        int childCount = parent.getChildCount();
        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;
            int bottom = top + divider.getIntrinsicHeight();

            divider.setBounds(left, top, right, bottom);
            divider.draw(c);
        }
    }
}

You can either call the first constructor that uses the default Android divider attributes, or the second one that uses your own drawable, for example drawable/divider.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <size android:height="1dp" />
    <solid android:color="#ff992900" />
</shape>

Note: if you want the divider to be drawn over your items, override the onDrawOver() method instead.

Usage

To use your new class, add VerticalSpaceItemDecoration or DividerSpaceItemDecoration to RecyclerView, for example in your fragment's onCreateView() method:

private static final int VERTICAL_ITEM_SPACE = 48;
private RecyclerView recyclerView;
private LinearLayoutManager linearLayoutManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_feed, container, false);

    recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_home_recycler_view);
    linearLayoutManager = new LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(linearLayoutManager);

    //add ItemDecoration
    recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
    //or
    recyclerView.addItemDecoration(new DividerItemDecoration(getActivity()));
    //or
    recyclerView.addItemDecoration(
            new DividerItemDecoration(getActivity(), R.drawable.divider));

    recyclerView.setAdapter(...);

    return rootView;
}

There's also Lucas Rocha's library which is supposed to simplify the item decoration process. I haven't tried it though.

Among its features are:

  • A collection of stock item decorations including:
  • Item spacing Horizontal/vertical dividers.
  • List item
Selfcongratulation answered 20/11, 2014 at 10:28 Comment(22)
You should not create objects in onDraw if possible as it's costly and onDraw is called often. Create them in the constructor for use later on.Betti
@Betti Correct me if I'm wrong, but I don't create any objects in onDraw(). I just reference already existing instances.Selfcongratulation
Sorry @Selfcongratulation you're right. I always avoid references creation in onDraw also. I saw you were doing it and I misread it as Object creation. I believe that you should avoid this also as it creates multiple pointers to the same resource but I may be wrong.Betti
I wonder if it is a good idea to use Paint instead of creating a drawable? Then I can call canvas.drawLine(startX, startY, stopX, stopY, mPaint) in onDrawOver? Any performance difference?Milliken
Just an informative comment: always add the space to the last item if you plan to add items later on you list. If you don't do that, when adding an item, it will not have the space. Thanks for the VerticalSpace!Upset
How do I make it work for GridLayoutManager, for showing vertical dividers between cells too? The code seems to work fine only for lists (horizontal divider, between rows...).Jillian
DividerItemDecoration as shown above won't work if the items are fully opaque, dividers will get overdrawn by the items. In that case you need to override the getItemOffsets() too and add bottom offset to outRect so the divider ends up outside of the item. Alternatively, you can override onDrawOver() instead of onDraw() to draw the divider afeter the item.Nonie
A whole page of code to just add divider to recyclerView is the best answer. Shame on you, google.Brandiebrandise
If you use wrap_content on your recyclerview then the bottom divider will not show...Kneepad
This is by far the best explanation I could find, it just lacks one explanation: what is the advantage of using ItemDecoration if you only want to add a simple divider/separator (just a View of height 1 or similar, below your last item view). Is there an advantage, even if its a simple divider like this, or does it become more advantageous if you want to achieve something more complex?Downcast
@Selfcongratulation is it possible to add padding to the line? like left and right padding so the line doesnt start at the beginning and go to the end?Swat
Also wondering if its possible to add margin or padding to the line, nothing I have tried works so far so wondering if someone else has a solutionPrimogeniture
This is an old question, but I found out something recently that is not covered here. If you have an item animator, then the lines may stick around while the animation is happening. I changed the int top = ... to this final int ty = (int) (child.getTranslationY() + 0.5f); int top = child.getBottom() + params.bottomMargin + ty; Hope that helps someone.Someday
to add space after last item you can use android:clipToPadding="false" android:paddingBottom="75dp"Gandzha
Good answer and I followed your instructions but ran into a peculiar situation. When I refresh my dataset and call setAdapter (or swapAdapter) it seems that the getitemOffsets() method is called again and I'm adding offsets to a view that already had offsets added. The next effect is that the padding that was added in the first pass is doubled with each refresh. Have you ran into this and/or have any ideas on how to avoid that?Sherrell
This does not work if you just want an invisible divider :(Tensity
To simply change divider color add this line to constructor mDivider.setColorFilter(color, PorterDuff.Mode.SRC_OVER)Portwin
Hi, I found that if I set the outRect.top = mVerticalSpaceHeight will not add margin after the divider, may I know what's wrong with that?Fraunhofer
To add in inset to the divider, see this answer.Cham
This approach draws the divider/spacing on the list item itself. This means that if I have drag & drop or swipe functionality, the thing drawn by item decorator will move(on drag/swipe) with the item itself. Is there a way to draw spaces/dividers as separate views in the RecyclerView to override this behavior?Gaullism
the divider in the support library draws after the last element as well :/Soninlaw
I find this adds a lot of lag to my recycler viewWant
R
573

Just add

recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
                DividerItemDecoration.VERTICAL));

Also you may need to add the dependency
implementation 'com.android.support:recyclerview-v7:28.0.0'

For customizing it a little bit you can add a custom drawable:

DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
itemDecorator.setDrawable(ContextCompat.getDrawable(getContext(), R.drawable.divider));

You are free to use any custom drawable, for instance:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <solid android:color="@color/colorPrimary"/>
    <size android:height="0.5dp"/>
</shape>
Randallrandan answered 17/12, 2016 at 18:40 Comment(8)
Activity is not needed. Context is enoughRenz
This must be the correct answer. Plz, change getActivity To just context.Aldora
Also it is better to get orientation from your LayoutManager.Strip
Thank you! Also you can use Configuration for vertical divider: if (orientation == Configuration.ORIENTATION_LANDSCAPE) { recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL)); } else { recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));}Lancelancelet
This "Also you may need to add the dependency compile 'com.android.support:recyclerview-v7:27.1.0'" helped me to become human again.Noontide
Nice answer, but it adds a divider also after the last item.Hippie
What different if i add marginBottom for item?)Nock
@Nock I assume your question was directed at CoolMind's comment. While this will be sufficient for most use cases, if the size of a divider is large, it will at the very least be pretty ugly to have your RecyclerView extend to an unnecessary length, just to accommodate the last unneeded divider. If e.g. you would instead let the Adapter add LayoutParams with a margin in onBindViewHolder, then you could exclude the first/last element respectively and would only have a divider between two elements. But as I said, this wont be noticeable with small dividers, so its ok.Breakneck
Y
276

Might I direct your attention to this particular file on GitHub by Alex Fu: link

It's the DividerItemDecoration.java example file "pulled straight from the support demos".

I was able to get divider lines nicely after importing this file in my project and add it as an item decoration to the recycler view.

Here's how my onCreateView look like in my fragment containing the Recyclerview:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_recycler_view, container, false);

    mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
    mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));

    mRecyclerView.setHasFixedSize(true);
    mLayoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());

    return rootView;
}

I'm sure additional styling can be done, but it's a starting point. :)

Yare answered 21/7, 2014 at 18:29 Comment(12)
How do you add replace those: "footerDividersEnabled", "headerDividersEnabled", "listSelector", "fastScrollEnabled", "smoothScrollbar", "textFilterEnabled" ?Jillian
Any inputs on How to put Styling?Laughing
to style this solution you need to override "android:listDivider" attribute in your themeHowe
But how to add a divider only before last item?Magician
How to apply left padding to the divider to omit the area below the icon like here: material-design.storage.googleapis.com/publish/v_2/…Pumpkin
How can we customize the divider like height, color, ... ? thxRozellarozelle
apparently divider is not appearingCastellatus
Divider doesn't work with RecyclerView. You need to use RecyclerView.itemDecoration. See this answer : https://mcmap.net/q/27078/-how-to-add-dividers-and-spaces-between-items-in-recyclerviewRozellarozelle
@Cocorico, i have checked that answer already, it gives space between views of recyclerview, I need a line to appear like a divider in listviewCastellatus
@PankajNimgade So I don't know. I haven't use ItemDecoration for RecyclerView yet. SorryRozellarozelle
why does the divider expands the whole width of the item? how to display like in the specs google.com/design/spec/components/lists.html#lists-specsEpos
mRecyclerView.setItemAnimator(new DefaultItemAnimator()); has no effect here.Passbook
Z
173

A simple ItemDecoration implementation for equal spaces between all items:

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private int space;

    public SpacesItemDecoration(int space) {
        this.space = space;
    }

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

        // Add top margin only for the first item to avoid double space between items
        if(parent.getChildAdapterPosition(view) == 0) {
            outRect.top = space;
        }
    }
}
Zn answered 27/12, 2014 at 3:8 Comment(8)
i am getting the space but how do i get a dividerCastellatus
getChildPosition is now deprecated, getChildAdapterPosition can be used instead.Selfcongratulation
Don't forget (as I did) to remove the call to super.getItemOffsets, or your offsets will be overwritten.Togetherness
@Selfcongratulation shouldn't the getChildLayoutPosition be used?Levison
This does not work if you need divider with some left\right paddingSatyr
does this implement the spacing in pixels?Oblique
About the top-space, how would you change it to also support FlexboxLayoutManager ?Jillian
Does this work for RTL and LTR? Because Rect explixilty operates on left and right.Eboat
C
115

The simple one is to set the background color for RecyclerView and a different background color for items. Here is an example...

<android.support.v7.widget.RecyclerView
    android:background="#ECEFF1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scrollbars="vertical"/>

And the TextView item (it can be anything though) with bottom margin "x" dp or px.

<TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="1dp"
    android:background="#FFFFFF"/>

The output...

Enter image description here

Cygnet answered 5/7, 2015 at 3:59 Comment(4)
What a trick! only needs keeping the list white while loading.Grower
Beware of overdraw!Progestational
@Progestational could you elaborate?Irma
When drawing in Android couple of layers one above the other (the activity background, the recycle view background and the item view background)- Android drawing them all, also the ones that not visible to the users. That called overdraw and might affect your performance, more about it here: youtube.com/watch?v=T52v50r-JfEProgestational
L
52

This is simple, and you don't need such complicated code:

DividerItemDecoration divider = new DividerItemDecoration(
    mRVMovieReview.getContext(), DividerItemDecoration.VERTICAL
);
divider.setDrawable(
    ContextCompat.getDrawable(getBaseContext(), R.drawable.line_divider)
);

mRVMovieReview.addItemDecoration(divider);

Add this in your drawable: line_divider.xml

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

    <size android:height="1dp" />
    <solid android:color="@android:color/black" />
</shape>
Lam answered 28/8, 2017 at 7:48 Comment(0)
P
45

The way how I'm handling the Divider view and also Divider Insets is by adding a RecyclerView extension.

1.

Add a new extension file by naming View or RecyclerView:

RecyclerViewExtension.kt

and add the setDivider extension method inside the RecyclerViewExtension.kt file.

/*
* RecyclerViewExtension.kt
* */
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView


fun RecyclerView.setDivider(@DrawableRes drawableRes: Int) {
    val divider = DividerItemDecoration(
        this.context,
        DividerItemDecoration.VERTICAL
    )
    val drawable = ContextCompat.getDrawable(
        this.context,
        drawableRes
    )
    drawable?.let {
        divider.setDrawable(it)
        addItemDecoration(divider)
    }
}

2.

Create a Drawable resource file inside of drawable package like recycler_view_divider.xml:

<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:insetLeft="10dp"
    android:insetRight="10dp">

    <shape>
        <size android:height="0.5dp" />
        <solid android:color="@android:color/darker_gray" />
    </shape>

</inset>

where you can specify the left and right margin on android:insetLeft and android:insetRight.

3.

On your Activity or Fragment where the RecyclerView is initialized, you can set the custom drawable by calling:

recyclerView.setDivider(R.drawable.recycler_view_divider)

4.

Cheers 🍺

RecyclerView row with divider.

Piliform answered 17/10, 2019 at 11:23 Comment(0)
T
42

As I have set ItemAnimators. The ItemDecorator don't enter or exit along with the animation.

I simply ended up in having a view line in my item view layout file of each item. It solved my case. DividerItemDecoration felt to be too much of sorcery for a simple divider.

<View
    android:layout_width="match_parent"
    android:layout_height="1px"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:background="@color/lt_gray"/>
Throughcomposed answered 16/3, 2015 at 4:35 Comment(4)
You are right. Animations do not work with the ItemDecoration. I am not sure why, but without me specifying anything, I get animations and I find it very distracting and ugly that the lines created by ItemDecoration do not follow. So I will use a solution like yours.Burn
How did you deal with the last item ?Soninlaw
@Soninlaw . You pointed on right pain point. I would first get agree for a design to have divider on last item as well. But if you don't want that. assign a id to this view and hide in bindView if position is last.Throughcomposed
the simplest is the bestLedesma
M
42

I think using a simple divider will help you

To add divider to each item:

1. Add this to drawable directory line_divider.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
    android:width="1dp"
    android:height="1dp" />
<solid android:color="#999999" />
</shape>

2. Create SimpleDividerItemDecoration class

I used this example to define this class:

https://gist.github.com/polbins/e37206fbc444207c0e92

package com.example.myapp;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.example.myapp.R;

public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration{
    private Drawable mDivider;

    public SimpleDividerItemDecoration(Resources resources) {
        mDivider = resources.getDrawable(R.drawable.line_divider);
    }

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

        int childCount = parent.getChildCount();
        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;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

3. In activity or fragment that using RecyclerView, inside onCreateView add this:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
 RecyclerView myRecyclerView = (RecyclerView) layout.findViewById(R.id.my_recycler_view);
 myRecyclerView.addItemDecoration(new SimpleDividerItemDecoration(getResources()));
 ....
 }

4. To add spacing between Items

You just need to add padding property to your item view

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="4dp"
>
..... item structure
</RelativeLayout>
Mccormick answered 4/4, 2015 at 13:12 Comment(2)
How do I make it work for GridLayoutManager , for showing vertical dividers between the cells too?Jillian
resources.getDrawable() is now deprecated. You can pass in the context and use ContextCompat.getDrawable(context, R.drawable.line_divider)Ragnar
F
28

If anyone is looking to only add, say, 10 dp spacing between items, you can do so by setting a drawable to DividerItemDecoration:

DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
    recyclerView.getContext(),
    layoutManager.getOrientation()
);

dividerItemDecoration.setDrawable(
    ContextCompat.getDrawable(getContext(), R.drawable.divider_10dp)
);

recyclerView.addItemDecoration(dividerItemDecoration);

Where divider_10dpis a drawable resource containing:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <size android:height="10dp"/>
    <solid android:color="@android:color/transparent"/>
</shape>
Finfoot answered 17/2, 2017 at 13:12 Comment(0)
L
24

Since there is no right way to implement this yet properly using Material Design, I just did the following trick to add a divider on the list item directly:

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@color/dividerColor"/>
Laywoman answered 13/4, 2016 at 18:22 Comment(1)
DividerItemDecoration stop working after I had some elevation information of material design (to get the same effect like in Inbox) ; it got too complicated for a simple thing. This solution is simple and works.Campball
P
20

Instead of creating a shape xml for changing the divider height and color, you can create it programmatically like:

val divider = DividerItemDecoration(
                  context,
                  DividerItemDecoration.VERTICAL)

divider.setDrawable(ShapeDrawable().apply {
    intrinsicHeight = resources.getDimensionPixelOffset(R.dimen.dp_15)
    paint.color = Color.RED // Note:
                            //   Currently (support version 28.0.0), we
                            //   can not use tranparent color here. If
                            //   we use transparent, we still see a
                            //   small divider line. So if we want
                            //   to display transparent space, we
                            //   can set color = background color
                            //   or we can create a custom ItemDecoration
                            //   instead of DividerItemDecoration.
})

recycler_devices.addItemDecoration(divider)
Pirate answered 29/1, 2019 at 9:52 Comment(1)
this is useful answerVasty
N
15

OCTOBER 2016 UPDATE

With support library v25.0.0 there finally is a default implementation of basic horizontal and vertical dividers available!

DividerItemDecoration

Notice answered 20/10, 2016 at 8:12 Comment(0)
L
13

Add a margin to your view. It worked for me.

android:layout_marginTop="10dp"

If you just want to add equal spacing and want to do it in XML, just set padding to your RecyclerView and equal amount of layoutMargin to the item you inflate into your RecyclerView, and let the background color determine the spacing color.

Leeland answered 20/2, 2015 at 2:29 Comment(3)
Though this will work, this is not the correct answer, for example, because this doesnot solve the problem without doing extra things to the row layout, also, at the top the margin x1 will appear, between rows margin x2 will appear.Melanous
This isn't a good idea because the overscroll effect when pulling at the end of the list will have a unnecessary padding applied to it to when padding is applied to RecyclerViewHowlett
It is better to wrap the item's layout in a Support Library CardView so you can control other attributes e.g elevation/shadow, etc: <?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" card_view:cardElevation="4dp" <!-- your item's XML here --> </android.support.v7.widget.CardView>Multivocal
D
13

For those who are looking just for spaces between items in the RecyclerView, see my approach where you get equal spaces between all items, except in the first and last items where I gave a bigger padding. I only apply padding to left/right in a horizontal LayoutManager and to top/bottom in a vertical LayoutManager.

public class PaddingItemDecoration extends RecyclerView.ItemDecoration {

    private int mPaddingPx;
    private int mPaddingEdgesPx;

    public PaddingItemDecoration(Activity activity) {
        final Resources resources = activity.getResources();
        mPaddingPx = (int) resources.getDimension(R.dimen.paddingItemDecorationDefault);
        mPaddingEdgesPx = (int) resources.getDimension(R.dimen.paddingItemDecorationEdge);
    }

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

        final int itemPosition = parent.getChildAdapterPosition(view);
        if (itemPosition == RecyclerView.NO_POSITION) {
            return;
        }
        int orientation = getOrientation(parent);
        final int itemCount = state.getItemCount();

        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;

        /** Horizontal */
        if (orientation == LinearLayoutManager.HORIZONTAL) {
            /** All positions */
            left = mPaddingPx;
            right = mPaddingPx;

            /** First position */
            if (itemPosition == 0) {
                left += mPaddingEdgesPx;
            }
            /** Last position */
            else if (itemCount > 0 && itemPosition == itemCount - 1) {
                right += mPaddingEdgesPx;
            }
        }
        /** Vertical */
        else {
            /** All positions */
            top = mPaddingPx;
            bottom = mPaddingPx;

            /** First position */
            if (itemPosition == 0) {
                top += mPaddingEdgesPx;
            }
            /** Last position */
            else if (itemCount > 0 && itemPosition == itemCount - 1) {
                bottom += mPaddingEdgesPx;
            }
        }

        if (!isReverseLayout(parent)) {
            outRect.set(left, top, right, bottom);
        } else {
            outRect.set(right, bottom, left, top);
        }
    }

    private boolean isReverseLayout(RecyclerView parent) {
        if (parent.getLayoutManager() instanceof LinearLayoutManager) {
            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
            return layoutManager.getReverseLayout();
        } else {
            throw new IllegalStateException("PaddingItemDecoration can only be used with a LinearLayoutManager.");
        }
    }

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

File dimens.xml

<resources>
    <dimen name="paddingItemDecorationDefault">10dp</dimen>
    <dimen name="paddingItemDecorationEdge">20dp</dimen>
</resources>
Daman answered 25/5, 2015 at 15:41 Comment(0)
U
12
  • Here is a simple hack to add a divider

  • Just add a background to the layout of your recycler item as follows

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:background="@drawable/shape_border"
          android:gravity="center"
          android:orientation="horizontal"
          android:padding="5dp">
    
      <ImageView
          android:id="@+id/imageViewContactLogo"
          android:layout_width="60dp"
          android:layout_height="60dp"
          android:layout_marginRight="10dp"
          android:src="@drawable/ic_user" />
    
      <LinearLayout
          android:id="@+id/linearLayout"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_weight="0.92"
          android:gravity="center|start"
          android:orientation="vertical">
    
      <TextView
          android:id="@+id/textViewContactName"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:singleLine="true"
          android:text="Large Text"
          android:textAppearance="?android:attr/textAppearanceLarge" />
    
      <TextView
          android:id="@+id/textViewStatusOrNumber"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginTop="5dp"
          android:singleLine="true"
          android:text=""
          android:textAppearance="?android:attr/textAppearanceMedium" />
      </LinearLayout>
    
      <TextView
          android:id="@+id/textViewUnreadCount"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginRight="10dp"
          android:padding="5dp"
          android:text=""
          android:textAppearance="?android:attr/textAppearanceMedium"
          android:textColor="@color/red"
          android:textSize="22sp" />
    
      <Button
          android:id="@+id/buttonInvite"
          android:layout_width="54dp"
          android:layout_height="wrap_content"
          android:background="@drawable/ic_add_friend" />
      </LinearLayout>
    

Create the following shape_border.xml file in the drawable folder:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle" >
    <gradient
         android:angle="270"
         android:centerColor="@android:color/transparent"
         android:centerX="0.01"
         android:startColor="#000" />
</shape>

Here is the final result - a RecyclerView with divider.

Here is final result - a RecyclerView with divider.

Urissa answered 30/7, 2015 at 9:36 Comment(3)
This is not a preferred approach. Although @Selfcongratulation answer initiates int's in the onDraw and parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1 should probably be parent.getChildAdapterPosition(view) > 0 with outRect.bottom = mVerticalSpaceHeight becoming outRect.top = mVerticalSpaceHeight it should be the accepted answer.Betti
@Betti - You can't neglect it by only saying this is not a preferred approach, it gives me accurate results as expected, I also look at EyesClear's answer but that answer is too complicated for a simple divider, however if there is need to do some extra decoration with item then that can be the accepted answer.Urissa
For the down voters, this answer was given at the time when there were no official classes for DividerItemDecoration, so just compare the time gap between this answer and following answer given by Leo Droidcoder. :)Urissa
P
11

This doesn't actually solve the problem, but as a temporary workaround, you can set the useCompatPadding property on the card in your XML layout to make it measure the same as it does on pre-Lollipop versions.

card_view:cardUseCompatPadding="true"
Psephology answered 13/12, 2014 at 20:13 Comment(1)
P
9

I forked the DividerItemDecoration from an older gist and simplified it to fit my use case, and I also modified it to draw the dividers the way they are drawn in ListView, including a divider after the last list item. This will also handle vertical ItemAnimator animations:

1) Add this class to your project:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    private Drawable divider;

    public DividerItemDecoration(Context context) {
        try {
            final TypedArray a = context.obtainStyledAttributes(ATTRS);
            divider = a.getDrawable(0);
            a.recycle();
        } catch (Resources.NotFoundException e) {
            // TODO Log or handle as necessary.
        }
    }

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

        if (getOrientation(parent) == LinearLayoutManager.VERTICAL)
            outRect.top = divider.getIntrinsicHeight();
        else
            throw new IllegalArgumentException("Only usable with vertical lists");
    }

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

        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        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 size = divider.getIntrinsicHeight();
            final int top = (int) (child.getTop() - params.topMargin - size + child.getTranslationY());
            final int bottom = top + size;
            divider.setBounds(left, top, right, bottom);
            divider.draw(c);

            if (i == childCount - 1) {
                final int newTop = (int) (child.getBottom() + params.bottomMargin + child.getTranslationY());
                final int newBottom = newTop + size;
                divider.setBounds(left, newTop, right, newBottom);
                divider.draw(c);
            }
        }
    }

    private int getOrientation(RecyclerView parent) {
        if (!(parent.getLayoutManager() instanceof LinearLayoutManager))
            throw new IllegalStateException("Layout manager must be an instance of LinearLayoutManager");
        return ((LinearLayoutManager) parent.getLayoutManager()).getOrientation();
    }
}

2) Add the decorator to your RecylerView:

recyclerView.addItemDecoration(new DividerItemDecoration(getActivity()));
Piccalilli answered 4/8, 2015 at 20:41 Comment(1)
Correct, it's meant for a LinearLayoutManager. You can take the idea behind it to adapt it for a GridLayoutManager.Piccalilli
S
8

I feel like there's a need for a simple, code-based answer that doesn't use XML

DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);

ShapeDrawable shapeDrawableForDivider = new ShapeDrawable(new RectShape());

int dividerThickness = // (int) (SomeOtherView.getHeight() * desiredPercent);
shapeDrawableForDivider.setIntrinsicHeight(dividerThickness);
shapeDrawableForDivider.setAlpha(0);

dividerItemDecoration.setDrawable(shapeDrawableForDivider);

recyclerView.addItemDecoration(dividerItemDecoration);

I love this answer so much, I re-wrote it in a single-expression Kotlin answer:

    recyclerView.addItemDecoration(DividerItemDecoration(this,DividerItemDecoration.VERTICAL).also { deco ->
        with (ShapeDrawable(RectShape())){
            intrinsicHeight = (resources.displayMetrics.density * 24).toInt()
            alpha = 0
            deco.setDrawable(this)
        }
    })

This does the same thing as @Nerdy's original answer, except it sets the height of the divider to 24dp instead of a percentage of another view's height.

Straitjacket answered 26/9, 2018 at 7:51 Comment(0)
A
7

Taken from a Google search, add this ItemDecoration to your RecyclerView:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private Drawable mDivider;
    private boolean mShowFirstDivider = false;
    private boolean mShowLastDivider = false;


    public DividerItemDecoration(Context context, AttributeSet attrs) {
        final TypedArray a = context
                .obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider});
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider,
                                 boolean showLastDivider) {
        this(context, attrs);
        mShowFirstDivider = showFirstDivider;
        mShowLastDivider = showLastDivider;
    }

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

    public DividerItemDecoration(Drawable divider, boolean showFirstDivider,
                                 boolean showLastDivider) {
        this(divider);
        mShowFirstDivider = showFirstDivider;
        mShowLastDivider = showLastDivider;
    }

    @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.getChildPosition(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;
        }

        // Initialization needed to avoid compiler warning
        int left = 0, right = 0, top = 0, bottom = 0, size;
        int orientation = getOrientation(parent);
        int childCount = parent.getChildCount();

        if (orientation == LinearLayoutManager.VERTICAL) {
            size = mDivider.getIntrinsicHeight();
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
        } else { // Horizontal
            size = mDivider.getIntrinsicWidth();
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
        }

        for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            if (orientation == LinearLayoutManager.VERTICAL) {
                top = child.getTop() - params.topMargin;
                bottom = top + size;
            } else { // Horizontal
                left = child.getLeft() - params.leftMargin;
                right = left + size;
            }
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }

        // Show the last divider
        if (mShowLastDivider && childCount > 0) {
            View child = parent.getChildAt(childCount - 1);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            if (orientation == LinearLayoutManager.VERTICAL) {
                top = child.getBottom() + params.bottomMargin;
                bottom = top + size;
            } else { // hHorizontal
                left = child.getRight() + params.rightMargin;
                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.");
        }
    }
}
Adman answered 8/3, 2015 at 2:14 Comment(1)
This works nice only for LinearLayoutManager. What should be done for GridLayoutManager ?Jillian
T
7

You can easily add it programmatically.

If your Layout Manager is Linearlayout then you can use:

DividerItemDecoration is a RecyclerView.ItemDecoration that can be used as a divider between items of a LinearLayoutManager. It supports both HORIZONTAL and VERTICAL orientations.

mDividerItemDecoration =
  new DividerItemDecoration(recyclerView.getContext(),
                            mLayoutManager.getOrientation());
recyclerView.addItemDecoration(mDividerItemDecoration);

Source

Taveda answered 22/1, 2017 at 6:48 Comment(0)
A
7

Here's a decoration that lets you set a spacing between items as well as a spacing on the edges. This works for both HORIZONTAL and VERTICAL layouts.

class LinearSpacingDecoration(
    @Px private val itemSpacing: Int,
    @Px private val edgeSpacing: Int = 0
): RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val count = parent.adapter?.itemCount ?: 0
        val position = parent.getChildAdapterPosition(view)
        val leading = if (position == 0) edgeSpacing else itemSpacing
        val trailing = if (position == count - 1) edgeSpacing else 0
        outRect.run {
            if ((parent.layoutManager as? LinearLayoutManager)?.orientation == LinearLayout.VERTICAL) {
                top = leading
                bottom = trailing
            } else {
                left = leading
                right = trailing
            }
        }
    }
}

Usage:

recyclerView.addItemDecoration(LinearSpacingDecoration(itemSpacing = 10, edgeSpacing = 20))
Ardeliaardelis answered 15/10, 2020 at 16:14 Comment(0)
K
6

This link worked like a charm for me:

https://gist.github.com/lapastillaroja/858caf1a82791b6c1a36

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.util.AttributeSet;
import android.view.View;

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private Drawable mDivider;
    private boolean mShowFirstDivider = false;
    private boolean mShowLastDivider = false;


    public DividerItemDecoration(Context context, AttributeSet attrs) {
        final TypedArray a = context
                .obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider});
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider,
            boolean showLastDivider) {
        this(context, attrs);
        mShowFirstDivider = showFirstDivider;
        mShowLastDivider = showLastDivider;
    }

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

    public DividerItemDecoration(Drawable divider, boolean showFirstDivider,
            boolean showLastDivider) {
        this(divider);
        mShowFirstDivider = showFirstDivider;
        mShowLastDivider = showLastDivider;
    }

    @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.getChildPosition(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;
        }

        // Initialization needed to avoid compiler warning
        int left = 0, right = 0, top = 0, bottom = 0, size;
        int orientation = getOrientation(parent);
        int childCount = parent.getChildCount();

        if (orientation == LinearLayoutManager.VERTICAL) {
            size = mDivider.getIntrinsicHeight();
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
        } else { //horizontal
            size = mDivider.getIntrinsicWidth();
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
        }

        for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            if (orientation == LinearLayoutManager.VERTICAL) {
                top = child.getTop() - params.topMargin;
                bottom = top + size;
            } else { //horizontal
                left = child.getLeft() - params.leftMargin;
                right = left + size;
            }
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }

        // show last divider
        if (mShowLastDivider && childCount > 0) {
            View child = parent.getChildAt(childCount - 1);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            if (orientation == LinearLayoutManager.VERTICAL) {
                top = child.getBottom() + params.bottomMargin;
                bottom = top + size;
            } else { // horizontal
                left = child.getRight() + params.rightMargin;
                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.");
        }
    }
}

Then in your activity:

mCategoryRecyclerView.addItemDecoration(
    new DividerItemDecoration(this, null));

Or this if you are using a fragment:

mCategoryRecyclerView.addItemDecoration(
    new DividerItemDecoration(getActivity(), null));
Kneepad answered 21/8, 2015 at 16:33 Comment(2)
This works fine, but it does not show the divider under the last item in the list. I need it like this: mShowFirstDivider = false, mShowLastDivider = true, but it won't work. Any idea why?Flaunt
This can't handle GridLayoutManager well.Jillian
M
6

We can decorate the items using various decorators attached to the recyclerview such as the DividerItemDecoration:

Simply use the following ...taken from the answer byEyesClear:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable mDivider;

    /**
     * Default divider will be used
     */
    public DividerItemDecoration(Context context) {
        final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
        mDivider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    /**
     * Custom divider will be used
     */
    public DividerItemDecoration(Context context, int resId) {
        mDivider = ContextCompat.getDrawable(context, resId);
    }

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

        int childCount = parent.getChildCount();
        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;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

And then use the above as follows:

RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
recyclerView.addItemDecoration(itemDecoration);

This will display dividers between each item within the list as shown below:

Enter image description here

And for those of who are looking for more details can check out this guide Using the RecyclerView _ CodePath Android Cliffnotes.

Some answers here suggest the use of margins, but the catch is that:

If you add both top and bottom margins, they will appear both added between items and they will be too large. If you only add either, there will be no margin either at the top or the bottom of the whole list. If you add half of the distance at the top, half at the bottom, the outer margins will be too small.

Thus, the only aesthetically correct solution is the divider that the system knows where to apply properly: between items, but not above or below items.

Madelaine answered 20/9, 2015 at 12:40 Comment(3)
This doesn't demo what the DividerItemDecoration code looks like.Threonine
Its a AOSP class, I dug up the code for you ..... gist.githubusercontent.com/alexfu/0f464fc3742f134ccd1e/raw/…Madelaine
It doesn't work well: It doesn't handle different-height rows , and it doesn't show a vertical divider for gridsJillian
E
5

If you want to add the same space for items, the simplest way is to add top+left padding for RecycleView and right+bottom margins to card items.

File dimens.xml

<resources>
    <dimen name="divider">1dp</dimen>
</resources>

File list_item.xml

<CardView
    android:layout_marginBottom="@dimen/divider"
    android:layout_marginRight="@dimen/divider">

    ...
</CardView>

File list.xml

<RecyclerView
    ...
    android:paddingLeft="@dimen/divider"
    android:paddingTop="@dimen/divider" />
Encampment answered 28/5, 2015 at 18:14 Comment(0)
B
5

For GridLayoutManager I use this:

public class GridSpacesItemDecoration : RecyclerView.ItemDecoration
{
    private int space;

    public GridSpacesItemDecoration(int space) {
        this.space = space;
    }

    public override void GetItemOffsets(Android.Graphics.Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
    {
        var position = parent.GetChildLayoutPosition(view);

        /// Only for GridLayoutManager Layouts
        var manager = parent.GetLayoutManager() as GridLayoutManager;

        if (parent.GetChildLayoutPosition(view) < manager.SpanCount)
            outRect.Top = space;

        if (position % 2 != 0) {
            outRect.Right = space;
        }

        outRect.Left = space;
        outRect.Bottom = space;
    }
}

This works for any span count you have.

Backward answered 10/1, 2017 at 19:32 Comment(1)
About the top-space, how would you change it to also support FlexboxLayoutManager ?Jillian
D
5
public class CommonItemSpaceDecoration extends RecyclerView.ItemDecoration {

    private int mSpace = 0;
    private boolean mVerticalOrientation = true;

    public CommonItemSpaceDecoration(int space) {
        this.mSpace = space;
    }

    public CommonItemSpaceDecoration(int space, boolean verticalOrientation) {
        this.mSpace = space;
        this.mVerticalOrientation = verticalOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.top = SizeUtils.dp2px(view.getContext(), mSpace);
        if (mVerticalOrientation) {
            if (parent.getChildAdapterPosition(view) == 0) {
                outRect.set(0, SizeUtils.dp2px(view.getContext(), mSpace), 0, SizeUtils.dp2px(view.getContext(), mSpace));
            } else {
                outRect.set(0, 0, 0, SizeUtils.dp2px(view.getContext(), mSpace));
            }
        } else {
            if (parent.getChildAdapterPosition(view) == 0) {
                outRect.set(SizeUtils.dp2px(view.getContext(), mSpace), 0, 0, 0);
            } else {
                outRect.set(SizeUtils.dp2px(view.getContext(), mSpace), 0, SizeUtils.dp2px(view.getContext(), mSpace), 0);
            }
        }
    }
}

This will add space in every item's top and bottom (or left and right). Then you can set it to your recyclerView.

recyclerView.addItemDecoration(new CommonItemSpaceDecoration(16));

File SizeUtils.java

public class SizeUtils {
    public static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}
Dronski answered 11/2, 2017 at 6:16 Comment(0)
O
5

The newest approach is this one, being used for example like this in the onCreateView of a Fragment:

        val recyclerView = rootView.findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.adapter = mListAdapter
        recyclerView.layoutManager = LinearLayoutManager(context)
        rootView.context.let {
            val dividerItemDecoration = MaterialDividerItemDecoration(
                it,
                MaterialDividerItemDecoration.VERTICAL
            )
            dividerItemDecoration.isLastItemDecorated = false

            // https://github.com/material-components/material-components-android/blob/master/docs/components/Divider.md
            // Needed if you did not set colorOnSurface in your theme because otherwise the default color would be pink_900 -> default according to Material should be colorOnSurface (12% opacity applied automatically on top).
//            dividerItemDecoration.setDividerColorResource(it, R.color.colorDivider)

            recyclerView.addItemDecoration(dividerItemDecoration)
        }

I think you can forget all other solutions before.

Own answered 25/6, 2022 at 1:44 Comment(0)
D
4

I have added a line in a list item like below:

<View
    android:id="@+id/divider"
    android:layout_width="match_parent"
    android:layout_height="1px"
    android:background="@color/dividerColor"/>

"1px" will draw the thin line.

If you want to hide the divider for the last row, then use divider.setVisiblity(View.GONE); on the onBindViewHolder for the last list Item.

Dna answered 1/7, 2016 at 11:49 Comment(1)
I prefer this one, others' are just too complicated.Niche
R
4
  1. One of the ways is by using the cardview and recycler view together. We can easily add an effect, like a divider. Example: Create dynamic lists with RecyclerView

  2. And another is by adding a view as a divider to a list_item_layout of a recycler view.

     <View
         android:id="@+id/view1"
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="@color/colorAccent" />
    
Rufena answered 31/1, 2017 at 8:43 Comment(0)
B
4

Here's how I would do it in Kotlin, for a simple space between Items with a default size of 10 dp:

class SimpleItemDecoration(context: Context, space: Int = 10) : RecyclerView.ItemDecoration() {

    private val spaceInDp = ConvertUtil.dpToPx(context, space)

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {

        outRect.left = spaceInDp
        outRect.right = spaceInDp
        outRect.bottom = spaceInDp
        // Add top margin only for the first item to avoid double space between items
        if (parent.getChildAdapterPosition(view) == 0) {
            outRect.top = spaceInDp
        }
    }
}

And here is the dpToPx method:

fun dpToPx(context: Context, dp: Int): Int {
        return (dp * context.resources.displayMetrics.density).toInt()
    }

and then add it to the RecyclerView like this: (thanks @MSpeed)

recyclerView.addItemDecoration(SimpleItemDecoration(context))
Bowne answered 16/2, 2020 at 1:33 Comment(1)
Works great. Just needs: recyclerView.addItemDecoration(SimpleItemDecoration(context))Selwyn
R
4

In order to accomplish spacing between items in a RecylerView, we can use ItemDecorators:

addItemDecoration(object : RecyclerView.ItemDecoration() {

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State,
    ) {
        super.getItemOffsets(outRect, view, parent, state)
        if (parent.getChildAdapterPosition(view) > 0) {
            outRect.top = 8.dp // Change this value with anything you want. Remember that you need to convert integers to pixels if you are working with dps :)
        }
    }
})

A few things to have in consideration given the code I pasted:

  • You don't really need to call super.getItemOffsets but I chose to, because I want to extend the behavior defined by the base class. If the library got an update doing more logic behind the scenes, we would miss it.

  • As an alternative to adding top spacing to the Rect, you could also add bottom spacing, but the logic related to getting the last item of the adapter is more complex, so this might be slightly better.

  • I used an extension property to convert a simple integer to dps: 8.dp. Something like this might work:

val Int.dp: Int
    get() = (this * Resources.getSystem().displayMetrics.density + 0.5f).toInt()

// Extension function works too, but invoking it would become something like 8.dp()
Redhot answered 9/9, 2020 at 16:24 Comment(0)
U
3

The RecyclerView is a bit different from the ListView. Actually, the RecyclerView needs a ListView like structure in it. For example, a LinearLayout. The LinearLayout has parameters for the dividing each element.

In the code below I have a RecyclerView comprised of CardView objects within a LinearLayout with a "padding" that will put some space between items. Make that space really small and you get a line.

Here's the Recycler view in file recyclerview_layout.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".ToDoList">

    <!-- A RecyclerView with some commonly used attributes -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/todo_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

And here is what each item looks like (and it shows as divided due to the android:padding in the LinearLayout that surrounds everything) in another file: cards_layout.xml

<?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"

    **android:padding="@dimen/activity_vertical_margin"**>

    <!-- A CardView that contains a TextView -->
    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:elevation="30dp"
        card_view:cardElevation="3dp">
        <TextView
            android:id="@+id/info_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
        />
    </android.support.v7.widget.CardView>
</LinearLayout>
Uppermost answered 25/7, 2015 at 5:33 Comment(0)
R
3

A really easy solution is to use RecyclerView-FlexibleDivider

Add dependency:

compile 'com.yqritc:recyclerview-flexibledivider:1.4.0'

Add to your recyclerview:

recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(context).build());

And you're done!

Refrigerant answered 22/9, 2016 at 12:38 Comment(1)
works like a charm... gotta love open-source sharing.Electronics
S
2

Implement its own version of RecyclerView.ItemDecoration

public class SpacingItemDecoration extends RecyclerView.ItemDecoration {
    private int spacingPx;
    private boolean addStartSpacing;
    private boolean addEndSpacing;

    public SpacingItemDecoration(int spacingPx) {
        this(spacingPx, false, false);
    }

    public SpacingItemDecoration(int spacingPx, boolean addStartSpacing, boolean addEndSpacing) {
        this.spacingPx = spacingPx;
        this.addStartSpacing = addStartSpacing;
        this.addEndSpacing = addEndSpacing;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (spacingPx <= 0) {
            return;
        }

        if (addStartSpacing && parent.getChildLayoutPosition(view) < 1 || parent.getChildLayoutPosition(view) >= 1) {
            if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
                outRect.top = spacingPx;
            } else {
                outRect.left = spacingPx;
            }
        }

        if (addEndSpacing && parent.getChildAdapterPosition(view) == getTotalItemCount(parent) - 1) {
            if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {
                outRect.bottom = spacingPx;
            } else {
                outRect.right = spacingPx;
            }
        }
    }

    private int getTotalItemCount(RecyclerView parent) {
        return parent.getAdapter().getItemCount();
    }

    private int getOrientation(RecyclerView parent) {
        if (parent.getLayoutManager() instanceof LinearLayoutManager) {
            return ((LinearLayoutManager) parent.getLayoutManager()).getOrientation();
        } else {
            throw new IllegalStateException("SpacingItemDecoration can only be used with a LinearLayoutManager.");
        }
    }
}
Sheath answered 24/6, 2015 at 17:9 Comment(0)
A
2

RecyclerView doesn't provide a straightforward interface for drawing dividers of lists. But actually it provides us a much more flexible way to draw dividers.

We use RecyclerView.ItemDecoration to decorate RecyclerView's tiles with dividers or anything you want. That is also why it is called ItemDecoration.

As described in Material Design Guidelines:

The divider sits within the baseline of the tile.

So if you want to follow the Material Design Guidelines, you don't need extra space to draw the dividers. Just draw them on the tiles. However, you have the right to do whatever you want to do. So I implemented one which gives you the ability to set insets as well as draw on/underneath the tiles.

public class InsetDivider extends RecyclerView.ItemDecoration {

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    private Paint mPaint;
    // in pixel
    private int mDividerHeight;
    // left inset for vertical list, top inset for horizontal list
    private int mFirstInset;
    // right inset for vertical list, bottom inset for horizontal list
    private int mSecondInset;
    private int mColor;
    private int mOrientation;
    // set it to true to draw divider on the tile, or false to draw beside the tile.
    // if you set it to false and have inset at the same time, you may see the background of
    // the parent of RecyclerView.
    private boolean mOverlay;

    private InsetDivider() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
    }

    public int getDividerHeight() {
        return mDividerHeight;
    }

    public void setDividerHeight(int dividerHeight) {
        this.mDividerHeight = dividerHeight;
    }

    public int getFirstInset() {
        return mFirstInset;
    }

    public void setFirstInset(int firstInset) {
        this.mFirstInset = firstInset;
    }

    public int getSecondInset() {
        return mSecondInset;
    }

    public void setSecondInset(int secondInset) {
        this.mSecondInset = secondInset;
    }

    public int getColor() {
        return mColor;
    }

    public void setColor(int color) {
        this.mColor = color;
        mPaint.setColor(color);
    }

    public int getOrientation() {
        return mOrientation;
    }

    public void setOrientation(int orientation) {
        this.mOrientation = orientation;
    }

    public boolean getOverlay() {
        return mOverlay;
    }

    public void setOverlay(boolean overlay) {
        this.mOverlay = overlay;
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    protected void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft() + mFirstInset;
        final int right = parent.getWidth() - parent.getPaddingRight() - mSecondInset;
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            if (parent.getChildAdapterPosition(child) == (parent.getAdapter().getItemCount() - 1)) {
                continue;
            }
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int bottom;
            final int top;
            if (mOverlay) {
                bottom = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
                top = bottom - mDividerHeight;
            } else {
                top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
                bottom = top + mDividerHeight;
            }
            c.drawRect(left, top, right, bottom, mPaint);
        }
    }

    protected void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop() + mFirstInset;
        final int bottom = parent.getHeight() - parent.getPaddingBottom() - mSecondInset;
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            if (parent.getChildAdapterPosition(child) == (parent.getAdapter().getItemCount() - 1)) {
                continue;
            }
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
            final int right;
            final int left;
            if (mOverlay) {
                right = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
                left = right - mDividerHeight;
            } else {
                left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
                right = left + mDividerHeight;
            }
            c.drawRect(left, top, right, bottom, mPaint);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOverlay) {
            super.getItemOffsets(outRect, view, parent, state);
            return;
        }

        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDividerHeight);
        } else {
            outRect.set(0, 0, mDividerHeight, 0);
        }
    }

    /**
     * Handy builder for creating {@link InsetDivider} instance.
     */
    public static class Builder {

        private Context mContext;
        private int mDividerHeight;
        private int mFirstInset;
        private int mSecondInset;
        private int mColor;
        private int mOrientation;
        private boolean mOverlay = true; // set default to true to follow Material Design Guidelines

        public Builder(Context context) {
            mContext = context;
        }

        public Builder dividerHeight(int dividerHeight) {
            mDividerHeight = dividerHeight;
            return this;
        }

        public Builder insets(int firstInset, int secondInset) {
            mFirstInset = firstInset;
            mSecondInset = secondInset;
            return this;
        }

        public Builder color(@ColorInt int color) {
            mColor = color;
            return this;
        }

        public Builder orientation(int orientation) {
            mOrientation = orientation;
            return this;
        }

        public Builder overlay(boolean overlay) {
            mOverlay = overlay;
            return this;
        }

        public InsetDivider build() {
            InsetDivider insetDivider = new InsetDivider();

            if (mDividerHeight == 0) {
                // Set default divider height to 1dp.
                 insetDivider.setDividerHeight(mContext.getResources().getDimensionPixelSize(R.dimen.divider_height));
            } else if (mDividerHeight > 0) {
                insetDivider.setDividerHeight(mDividerHeight);
            } else {
                throw new IllegalArgumentException("Divider's height can't be negative.");
            }

            insetDivider.setFirstInset(mFirstInset < 0 ? 0 : mFirstInset);
            insetDivider.setSecondInset(mSecondInset < 0 ? 0 : mSecondInset);

            if (mColor == 0) {
                throw new IllegalArgumentException("Don't forget to set color");
            } else {
                insetDivider.setColor(mColor);
            }

            if (mOrientation != InsetDivider.HORIZONTAL_LIST && mOrientation != InsetDivider.VERTICAL_LIST) {
                throw new IllegalArgumentException("Invalid orientation");
            } else {
                insetDivider.setOrientation(mOrientation);
            }

            insetDivider.setOverlay(mOverlay);

            return insetDivider;
        }
    }
}

And you can use it like this:

ItemDecoration divider = new InsetDivider.Builder(this)
                            .orientation(InsetDivider.VERTICAL_LIST)
                            .dividerHeight(getResources().getDimensionPixelSize(R.dimen.divider_height))
                            .color(getResources().getColor(R.color.colorAccent))
                            .insets(getResources().getDimensionPixelSize(R.dimen.divider_inset), 0)
                            .overlay(true)
                            .build();

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(divider);

I also wrote a demo app about how to implement ItemDecoration and how to use them. You can check out my GitHub repository Dividers-For-RecyclerView. There are three implementations in it:

  • UnderneathDivider
  • OverlayDivider
  • InsetDivider
Achates answered 27/9, 2016 at 15:4 Comment(0)
V
2

Using RecyclerView with ItemDecorations - A Basic Separator Sample in Kotlin android

A complete sample including a builder, the possibility to add margins or use a resource color

    class SeparatorDecoration constructor(ctx: Context, @ColorRes dividerColor: Int, heightDp: Float) :
    RecyclerView.ItemDecoration() {

    private val mPaints = Paint()

    init {
        mPaints.color = dividerColor
        val thickness = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            heightDp,
            ctx.resources.displayMetrics
        )
        mPaints.strokeWidth = thickness
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val params = view.layoutParams as RecyclerView.LayoutParams

        // and add a separator to any view but the last one
        val p = params.viewAdapterPosition

        // and add a separator to any view but the last one
        if (p < state.itemCount) outRect[0, 0, 0] =
            mPaints.strokeWidth.toInt() // left, top, right, bottom
        else outRect.setEmpty() // 0, 0, 0, 0
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {

        // we set the stroke width before, so as to correctly draw the line we have to offset by width / 2
        val offset = (mPaints.strokeWidth / 2).roundToInt()

        // this will iterate over every visible view
        for (i in 0 until parent.childCount) {
            // get the view
            val view: View = parent.getChildAt(i)
            val params = view.layoutParams as RecyclerView.LayoutParams

            // get the position
            val position = params.viewAdapterPosition

            // and finally draw the separator
            if (position < state.itemCount) {
                c.drawLine(
                    view.left.toFloat(),
                    (view.bottom + offset).toFloat(),
                    view.right.toFloat(), (view.bottom + offset).toFloat(), mPaints
                )
            }
        }
    }
  }

The setup is easy. Just add your decorations along with the rest of the initial setup for your recyclerView.

 val decoration = SeparatorDecoration(context, R.color.primaryColor, 1.5f)
 recyclerview.addItemDecoration(decoration)
Vogue answered 4/8, 2022 at 8:20 Comment(0)
S
1

I have a very simple way of adding a divider in RecyclerView. Use a custom adapter to modify the recycler view layout and then along with the recycler view items add LinearLayout with a background color (which will be the divider color) and add a height of 1 dp (or as per your requirement) and width to match parent.

Here is a sample code.

<?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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="18dp">

        <TextView
            android:id="@+id/list_row_SNO"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight=".8"
            android:layout_gravity="end"
            android:text="44."
            android:textAlignment="center"
            android:textSize="24sp"
            android:textColor="@color/colorBlack"
            android:fontFamily="sans-serif-condensed" />

        <TextView
            android:id="@+id/list_row_Heading"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight=".2"
            android:layout_gravity="start"
            android:text="Student's application for leave and this what"
            android:textAlignment="textStart"
            android:textSize="24sp"
            android:textColor="@color/colorBlack"
            android:fontFamily="sans-serif-condensed" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorHighlight">
    </LinearLayout>

</LinearLayout>
Stinnett answered 4/2, 2017 at 9:7 Comment(0)
S
1
public class VerticalItemDecoration extends RecyclerView.ItemDecoration {

    private boolean verticalOrientation = true;
    private int space = 10;

    public VerticalItemDecoration(int value, boolean verticalOrientation) {
        this.space = value;
        this.verticalOrientation = verticalOrientation;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                               RecyclerView.State state) {
        // Skip first item in the list
        if (parent.getChildAdapterPosition(view) != 0) {
            if (verticalOrientation) {
                outRect.set(space, 0, 0, 0);
            } else if (!verticalOrientation) {
                outRect.set(0, space, 0, 0);
            }
        }
    }
}

mCompletedShippingRecyclerView.addItemDecoration(new VerticalItemDecoration(20,false));
Sowder answered 9/2, 2017 at 11:16 Comment(1)
Please include some expanation on why & how your answer solvers the OP's problem.Selfmortification
T
1

Use this class to set divider in your RecyclerView.

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

    private int spanCount;
    private int spacing;
    private boolean includeEdge;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        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
            }
        }
    }
}
Trichotomy answered 14/3, 2017 at 11:56 Comment(1)
About the top-space, how would you change it to also support FlexboxLayoutManager ?Jillian
M
1

PrimeAdapter

By using PrimeAdapter, handling dividers in RecyclerViews could be so simple. It provides multiple functionality and more flexibility to create and manage dividers.

You can create an adapter as following (please see the complete documentation about usage in GitHub):

val adapter = PrimeAdapter.with(recyclerView)
                    .setLayoutManager(LinearLayoutManager(activity))
                    .set()
                    .build(ActorAdapter::class.java)

After creating an adapter instance, you can simply add a divider simply by using:

//----- Default divider:
adapter.setDivider()

//----- Divider with a custom drawable:
adapter.setDivider(ContextCompat.getDrawable(context, R.drawable.divider))

//----- Divider with a custom color:
adapter.setDivider(Color.RED)

//----- Divider with a custom color and a custom inset:
adapter.setDivider(Color.RED, insetLeft = 16, insetRight = 16)

//----- Deactivate dividers:
adapter.setDivider(null)

Enter image description here

Miosis answered 15/10, 2018 at 18:53 Comment(0)
J
1

If someone wants to set different values for spaceBetween, paddingLeft, paddingTop, paddingRight and paddingBottom.

class ItemPaddingDecoration(
    private val spaceBetween: Int,
    private val paddingLeft: Int = spaceBetween,
    private val paddingTop: Int = spaceBetween,
    private val paddingRight: Int = spaceBetween,
    private val paddingBottom: Int = spaceBetween
) : RecyclerView.ItemDecoration() {

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val position = parent.getChildAdapterPosition(view)
        val orientation = when (val layoutManager = parent.layoutManager) {
            is LinearLayoutManager -> {
                layoutManager.orientation
            }
            is GridLayoutManager -> {
                layoutManager.orientation
            }
            else -> {
                RecyclerView.HORIZONTAL
            }
        }
        if (orientation == RecyclerView.HORIZONTAL) {
            when {
                position == 0 -> {
                    outRect.set(paddingLeft, paddingTop, spaceBetween, paddingBottom)
                }
                position < parent.adapter!!.itemCount - 1 -> {
                    outRect.set(0, paddingTop, spaceBetween, paddingBottom)
                }
                else -> {
                    outRect.set(0, paddingTop, paddingRight, paddingBottom)
                }
            }
        } else {
            when {
                position == 0 -> {
                    outRect.set(paddingLeft, paddingTop, paddingRight, paddingBottom)
                }
                position < parent.adapter!!.itemCount - 1 -> {
                    outRect.set(paddingLeft, 0, paddingRight, spaceBetween)
                }
                else -> {
                    outRect.set(paddingLeft, 0, paddingRight, paddingBottom)
                }
            }
        }
    }
}
Jeromejeromy answered 3/2, 2022 at 10:11 Comment(0)
M
0

Here's my lazy approach, but it works: wrap the CardView in a layout and set a padding/margin on the parent layout to mimic the divider, and force the normal divider to null.

File list_item.xml

<LinearLayout
    android:id="@+id/entry_item_layout_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingBottom="<divider_size>" > // This is the divider
    <CardView
        android:layout_width="<width_size>"
        android:layout_height="<height_size>">
        ...
    </CardView>
</LinearLayout

File list.xml

<RecyclerView
    android:divider="@null"
    android:layout_width="<width_size>"
    android:layout_height="<height_size>"
    ...
/>
Multivocal answered 29/5, 2015 at 21:30 Comment(0)
B
0

This is how I would do it in Kotlin for a column display (grid)

  class ItemDecorationColumns(private val space: Int) : ItemDecoration() {

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val position = parent.getChildLayoutPosition(view)
        val manager = parent.layoutManager as GridLayoutManager?
        val spanCount = manager?.spanCount ?: 0

        if (position < spanCount) {
            outRect.top = space
        }

        if (position % 2 != 0) {
            outRect.right = space
        }
        outRect.left = space
        outRect.bottom = space
    }
}

Usage

rvAlbums.addItemDecoration(ItemDecorationColumns(resources.getDimensionPixelSize(R.dimen.space)))
Balenciaga answered 13/5, 2020 at 9:11 Comment(0)
B
-2

Just add this line after init in the recycler view object.

In Fragment:

mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(),android.R.drawable.divider_horizontal_bright));

In Activity

mRecyclerView.addItemDecoration(new DividerItemDecoration(this,android.R.drawable.divider_horizontal_bright));

Divider Item Decoration

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    public static final int VERTICAL_LIST = 0;

    public DividerItemDecoration(ListActivity listActivity, Object p1) {
    }

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable mDivider;

    /**
     * Default divider will be used
     */
    public DividerItemDecoration(Context context) {
        final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
        mDivider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    /**
     * Custom divider will be used
     */
    public DividerItemDecoration(Context context, int resId) {
        mDivider = ContextCompat.getDrawable(context, resId);
    }

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

        int childCount = parent.getChildCount();
        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;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}
Brodench answered 19/4, 2017 at 4:23 Comment(2)
The second parameter is not a drawable but int orientation.Pumpkin
@Pumpkin yes you were correct , Actually I forgot to upload my custom class , now I have updated, thanks to point it out.Brodench

© 2022 - 2024 — McMap. All rights reserved.