113

I have a quite simple RecyclerView.
This is how I set the divider:

DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
itemDecorator.setDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.news_divider));
recyclerView.addItemDecoration(itemDecorator);

And this is drawable/news_divider.xml:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@color/white_two"/>
    <size android:height="1dp"/>
</shape>

The problem is for some reason the divider is not just created in between the items. But also after the last item. And I want it only in between the items not after every item.

Any idea how to prevent the divider from showing after the last item?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Slobodan Antonijević
  • 2,533
  • 2
  • 17
  • 27

17 Answers17

154

Try this Code, it won't show divider for the last item. This method will give you more control over drawing divider.

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

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

    @Override
    public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        int dividerLeft = parent.getPaddingLeft();
        int dividerRight = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i <= childCount - 2; i++) {
            View child = parent.getChildAt(i);

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

            int dividerTop = child.getBottom() + params.bottomMargin;
            int dividerBottom = dividerTop + mDivider.getIntrinsicHeight();

            mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
            mDivider.draw(canvas);
        }
    }
}

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="@color/grey_300" />
</shape>

Set your Divider like this:

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(ContextCompat.getDrawable(context, R.drawable.divider));
recyclerView.addItemDecoration(dividerItemDecoration);
Bhuvanesh BS
  • 13,474
  • 12
  • 40
  • 66
75

If you don't like divider being drawn behind, you can simply copy or extend DividerItemDecoration class and change its drawing behaviour by modifying for (int i = 0; i < childCount; i++) to for (int i = 0; i < childCount - 1; i++)

Then add your decorator as recyclerView.addItemDecoration(your_decorator);


PREVIOUS SOLUTION:

As proposed here you can extend DividerItemDecoration like this:

recyclerView.addItemDecoration(
    new DividerItemDecoration(context, linearLayoutManager.getOrientation()) {
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view);
            // hide the divider for the last child
            if (position == state.getItemCount() - 1) {
                outRect.setEmpty();
            } else {
                super.getItemOffsets(outRect, view, parent, state);
            }
        }
    }
);

@Rebecca Hsieh pointed out:

This works when your item view in RecyclerView doesn't have a transparent background, for example,

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:background="#ffffff">
    ... 
</LinearLayout>

DividerItemDecoration.getItemOffsets is called by RecyclerView to measure the child position. This solution will put the last divider behind the last item. Therefore the item view in RecyclerView should have a background to cover the last divider and this makes it look like hidden.

Maksim Turaev
  • 4,115
  • 1
  • 29
  • 42
  • Nice and clean idea, but I liked onDraw overriding better as this didn't really behave as I wanted in outer class. – Slobodan Antonijević Sep 19 '17 at 22:04
  • 1
    This solution is cleaner than the accepted one because it relies on adapter position not child index. Also, to make it reusable replace anonymous inner class with public subclass e.g. `public class MiddleDividerItemDecoration extends DividerItemDecoration{...`. – Iwo Banas Feb 14 '18 at 09:10
  • 1
    If you wanna set default divider the above solution is ok. If you need more customisation like divider size and colour you have to create your own divider like the accepted answer @lwo Banas – Bhuvanesh BS Feb 19 '18 at 17:37
  • @IwoBanas Like I said this is a nice clean solution, but the accepted one gives much more posibilities. – Slobodan Antonijević Feb 22 '18 at 23:12
  • 20
    Not sure why, but outRect.setEmpty() is not working for me. – lostintranslation Feb 23 '18 at 17:23
  • @Iostintranslation I've updated an answer. Look if it helps. – Maksim Turaev Mar 13 '18 at 21:30
  • 3
    @MaksimTuraev I don't understand your code. If .setEmpty() sets the Rect to be 0,0,0,0, then why does it only work with a non transparent background? – Ruben2112 May 08 '18 at 09:03
  • @Ruben2112 Sorry, but I don't know the answer. If you will find out, feel free to edit, so the answer be more clear. – Maksim Turaev May 18 '18 at 06:03
  • 1
    @Ruben2112, setEmpty() on the last item says that it shouldn't be moved (no padding). As the divider is drawn under it, if the last item doesn't move, it hide the last divider. Unless it has a transparent background. – Glacoon Dec 12 '18 at 08:11
  • This is better solution if you're not trying to modify too much of the original DividerItemDecoration. – Robert Apr 12 '19 at 04:18
  • This for (int i = 0; i < childCount - 1; i++) one worked for me. – MobileEvangelist Dec 15 '19 at 10:56
  • @user924 try second solution then – Maksim Turaev Dec 19 '19 at 15:33
  • If you have a custom `layoutManager` with `reverseLayout = true`, then you need to check `position == 0` instead of `position == state.getItemCount() - 1`. – Westy92 Apr 24 '20 at 15:11
  • Doesn't this feel like a hack? I mean I was reading the point where the author mentions `This works when your item view in RecyclerView doesn't have a transparent background`. – capt.swag May 13 '21 at 14:59
  • @capt.swag I agree, it was first solution, so i decided not to remove it. Use the new one, it works without restrictions – Maksim Turaev May 13 '21 at 15:03
  • Sure, will use the new solution. – capt.swag May 13 '21 at 15:09
  • In my case the line `if (position == state.getItemCount() - 1)` was causing problems as the RecyclerState had less items than the adapter due to animations. So `if (parent.getChildAdapterPosition(view) == ((parent.adapter?.itemCount ?: Int.MAX_VALUE) - 1))` – Oknesif Jul 17 '23 at 16:52
16

Here is Kotlin version of accepted answer :

class DividerItemDecorator(private val divider: Drawable?) : RecyclerView.ItemDecoration() {

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val dividerLeft = parent.paddingLeft
        val dividerRight = parent.width - parent.paddingRight
        val childCount = parent.childCount
        for (i in 0..childCount - 2) {
            val child: View = parent.getChildAt(i)
            val params =
                child.layoutParams as RecyclerView.LayoutParams
            val dividerTop: Int = child.bottom + params.bottomMargin
            val dividerBottom = dividerTop + (divider?.intrinsicHeight?:0)
            divider?.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom)
            divider?.draw(canvas)
        }
    }
}
sma6871
  • 3,198
  • 3
  • 38
  • 52
15

The accepted answer doesn't allocate space for decoration as it does not override getItemOffsets()

I have tweaked the DividerItemDecoration from support library to exclude the decoration from the last item

public class DividerItemDecorator extends RecyclerView.ItemDecoration {

    private Drawable mDivider;
    private final Rect mBounds = new Rect();

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

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        canvas.save();
        final int left;
        final int right;
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

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

        if (parent.getChildAdapterPosition(view) == state.getItemCount() - 1) {
            outRect.setEmpty();
        } else
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
    }
}

To apply the decorator, use

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(dividerDrawable);
recyclerView.addItemDecoration(dividerItemDecoration);

The source for including orientation can be found here https://gist.github.com/abdulalin/146f8ca42aa8322692b15663b8d508ff

AbdulAli
  • 555
  • 3
  • 9
  • Thanks! Just posted below the Kotlin version of this working answer (with all new signature function of the original class). – Cyril Apr 30 '21 at 15:38
7

Extension function for Kotlin:

fun RecyclerView.addItemDecorationWithoutLastDivider() {

    if (layoutManager !is LinearLayoutManager)
        return

    addItemDecoration(object :
        DividerItemDecoration(context, (layoutManager as LinearLayoutManager).orientation) {

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

            if (parent.getChildAdapterPosition(view) == state.itemCount - 1)
                outRect.setEmpty()
            else
                super.getItemOffsets(outRect, view, parent, state)
        }
    })
}

You can use it easily:

recyclerView.addItemDecorationWithoutLastDivider()
seyfullah.bilgin
  • 1,421
  • 14
  • 16
6

Simplest way: Use MaterialDividerItemDecoration and set isLastItemDecorated to false

Milan Maji
  • 363
  • 1
  • 5
  • 9
5

Kotlin version and updated with new signature functions of the original DividerItemDecorator class of the working answer by AbdulAli :

class DividerItemDecorator(private val mDivider: Drawable) : ItemDecoration() {
    private val mBounds: Rect = Rect()

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        canvas.save()
        val left: Int
        val right: Int
        if (parent.clipToPadding) {
            left = parent.paddingLeft
            right = parent.width - parent.paddingRight
            canvas.clipRect(
                left, parent.paddingTop, right,
                parent.height - parent.paddingBottom
            )
        } else {
            left = 0
            right = parent.width
        }
        val childCount = parent.childCount
        for (i in 0 until childCount - 1) {
            val child: View = parent.getChildAt(i)
            parent.getDecoratedBoundsWithMargins(child, mBounds)
            val bottom: Int = mBounds.bottom + Math.round(child.getTranslationY())
            val top = bottom - mDivider.intrinsicHeight
            mDivider.setBounds(left, top, right, bottom)
            mDivider.draw(canvas)
        }
        canvas.restore()
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
            outRect.setEmpty()
        } else outRect.set(0, 0, 0, mDivider.intrinsicHeight)
    }
}
Cyril
  • 580
  • 2
  • 11
  • 23
5

Here is a Kotlin Extension Class:

    fun RecyclerView.addItemDecorationWithoutLastItem() {

    if (layoutManager !is LinearLayoutManager)
        return

    addItemDecoration(DividerItemDecorator(context))
 }

Here is the DividerItemDecorator Class

class DividerItemDecorator(context: Context) : ItemDecoration() {
    private val mDivider: Drawable = ContextCompat.getDrawable(context, R.drawable.divider)!!
    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val dividerLeft = parent.paddingLeft
        val dividerRight = parent.width - parent.paddingRight
        val childCount = parent.childCount
        for (i in 0..childCount - 2) {
            val child = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val dividerTop = child.bottom + params.bottomMargin
            val dividerBottom = dividerTop + mDivider.intrinsicHeight
            mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom)
            mDivider.draw(canvas)
        }
    }
}

Here is the 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="@color/your_color" />
</shape>

And finally call it like this

recyclerView.addItemDecorationWithoutLastItem()
Shogun Nassar
  • 606
  • 7
  • 16
4

Here's the DividerDecorator class i use in my apps which removes the bottom line of last item.

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

    public DividerDecorator(Context context) {
        mDivider = context.getResources().getDrawable(R.drawable.recyclerview_divider);
    }

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

You can set it to your RecyclerView with the following code:

mRecyclerViewEvent.addItemDecoration(new DividerDecorator(context));

Here's the recyclerview_divider.xml

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

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

savepopulation
  • 11,736
  • 4
  • 55
  • 80
4

I added support for both vertical and horizontal orientation (in Kotlin) based on DividerItemDecoration, inspired by some of the previous answers in this thread:

class CustomDividerItemDecorator(private val divider: Drawable, private val orientation: Int) : RecyclerView.ItemDecoration() {
    private val bounds: Rect = Rect()
        
    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.layoutManager == null) {
            return
        }
        if (orientation == DividerItemDecoration.VERTICAL) {
            drawVertical(canvas, parent)
        } else {
            drawHorizontal(canvas, parent)
        }
    }
    
    private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
        canvas.save()
        val left: Int
        val right: Int
        if (parent.clipToPadding) {
            left = parent.paddingLeft
            right = parent.width - parent.paddingRight
            canvas.clipRect(
                left, parent.paddingTop, right, parent.height - parent.paddingBottom
            )
        } else {
            left = 0
            right = parent.width
        }
        val childCount = parent.childCount
        for (i in 0 until childCount - 1) {
            val child: View = parent.getChildAt(i)
            parent.getDecoratedBoundsWithMargins(child, bounds)
            val bottom: Int = bounds.bottom + child.translationY.roundToInt()
            val top = bottom - divider.intrinsicHeight
            divider.setBounds(left, top, right, bottom)
            divider.draw(canvas)
        }
        canvas.restore()
    }
    
    private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
        canvas.save()
        val top: Int
        val bottom: Int
        if (parent.clipToPadding) {
            top = parent.paddingTop
            bottom = parent.height - parent.paddingBottom
            canvas.clipRect(
                parent.paddingLeft, top, parent.width - parent.paddingRight, bottom
            )
        } else {
            top = 0
            bottom = parent.height
        }
        val childCount = parent.childCount
        for (i in 0 until childCount - 1) {
            val child: View = parent.getChildAt(i)
            parent.getDecoratedBoundsWithMargins(child, bounds)
            val right: Int = bounds.right + child.translationX.roundToInt()
            val left = right - divider.intrinsicWidth
            divider.setBounds(left, top, right, bottom)
            divider.draw(canvas)
        }
        canvas.restore()
    }
    
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
            outRect.setEmpty()
        } else if (orientation == DividerItemDecoration.VERTICAL) {
            outRect.set(0, 0, 0, divider.intrinsicHeight)
        } else {
            outRect.set(0, 0, divider.intrinsicWidth, 0)
        }
    }
}

Usage:

val dividerItemDecoration = CustomDividerItemDecorator(
            ContextCompat.getDrawable(requireContext(), R.drawable.<DRAWABLE NAME>)!!,
            DividerItemDecoration.HORIZONTAL
    )
recyclerView.addItemDecoration(dividerItemDecoration)
Kozmotronik
  • 2,080
  • 3
  • 10
  • 25
Cqret
  • 91
  • 4
  • Despite the length, this is the best solution as of 2022! It keeps exactly what was needed from the original DividerItemDecoration class without being hacky, and reimplements it in Kotlin to handle (not necessarily just hide) the pesky final divider – Nick C Aug 07 '22 at 10:41
4

While many of the answers here have been helping enough, until the Google's MaterialDividerItemDecoration library come to help. With this library you don't even need to implement a custom class to control properties like insets and drawing for the last item etc. There you have a quick show case:

// layoutManager.getOrientation() can be used alternatively for the orientation parameter
MaterialDividerItemDecoration mdid = 
        new MaterialDividerItemDecoration(requireContext(), MaterialDividerItemDecoration.VERTICAL);
// Note that the inset value must be in pixels here let's say we want to inset 100px
mdid.setDividerInsetStart(100);
// And we don't want the item decorator to draw a divider for the last item in the list.
mdid.setLastItemDecorated(false);
recyclerView.addItemDecoration(mdid); // DONE
Kozmotronik
  • 2,080
  • 3
  • 10
  • 25
2

Create your own Divider class (Example here)

In the code that draws the divider, check first if you are drawing the divider for the last item in the list. If so, don't draw it.

Just be aware that if you override OnDrawOver it draws on TOP of your view including scrollbars etc. Best to stick to OnDraw. Lots of examples on Google but this is a good tutorial on creating your own decorators.

Kuffs
  • 35,581
  • 10
  • 79
  • 92
1

Try to set this item decorator to your RecyclerView

class NoLastItemDividerDecorator(
    val context: Context,
    orientation: Int
) : DividerItemDecoration(context, orientation) {

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

        val position = parent.getChildAdapterPosition(view)
        val last = parent.adapter?.itemCount ?: 0

        if (position == last - 1) {
            outRect.set(0, 0, 0, 0)
        } else {
            setDrawable(
                ContextCompat.getDrawable(
                    context,
                    R.drawable.your_divider_shape
                )
            )
        }
    }
}
Denys Lobur
  • 124
  • 5
1

The cleanest way is just to copy the original DividerItemDecoration class and put it inside androidx.recyclerview.widget package inside you app's java source. Then just change these lines inside the class (there are 2 of them - one for vertical and one for horizontal:

final int childCount = parent.getChildCount();

to

final int childCount = parent.getChildCount() -1;

No other code changes needed. Doing so will also retain all other properties and behaviors of DividerItemDecoration class.

Here's the modified class if you need it:

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package androidx.recyclerview.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
 * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
 * {@link #VERTICAL} orientations.
 *
 * <pre>
 *     mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
 *             mLayoutManager.getOrientation());
 *     recyclerView.addItemDecoration(mDividerItemDecoration);
 * </pre>
 */
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;

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

    private Drawable mDivider;

    /**
     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    private int mOrientation;

    private final Rect mBounds = new Rect();

    /**
     * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
     * {@link LinearLayoutManager}.
     *
     * @param context Current context, it will be used to access resources.
     * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        if (mDivider == null) {
            Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
                    + "DividerItemDecoration. Please set that attribute all call setDrawable()");
        }
        a.recycle();
        setOrientation(orientation);
    }

    /**
     * Sets the orientation for this divider. This should be called if
     * {@link RecyclerView.LayoutManager} changes orientation.
     *
     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
     */
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException(
                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
    }

    /**
     * Sets the {@link Drawable} for this divider.
     *
     * @param drawable Drawable that should be used as a divider.
     */
    public void setDrawable(@NonNull Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable cannot be null.");
        }
        mDivider = drawable;
    }

    /**
     * @return the {@link Drawable} for this divider.
     */
    @Nullable
    public Drawable getDrawable() {
        return mDivider;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getLayoutManager() == null || mDivider == null) {
            return;
        }
        if (mOrientation == VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    private void drawVertical(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int left;
        final int right;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount() -1;
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int top;
        final int bottom;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
            canvas.clipRect(parent.getPaddingLeft(), top,
                    parent.getWidth() - parent.getPaddingRight(), bottom);
        } else {
            top = 0;
            bottom = parent.getHeight();
        }

        final int childCount = parent.getChildCount() -1;
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
            final int right = mBounds.right + Math.round(child.getTranslationX());
            final int left = right - mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
        if (mDivider == null) {
            outRect.set(0, 0, 0, 0);
            return;
        }
        if (mOrientation == VERTICAL) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}
ltp
  • 366
  • 3
  • 10
0

This is a customized version of Android support DividerItemDecoration which ignore the last item:

https://gist.github.com/mohsenoid/8ffdfa53f0465533833b0b44257aa641

main difference is:

private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
    canvas.save()
    val left: Int
    val right: Int

    if (parent.clipToPadding) {
        left = parent.paddingLeft
        right = parent.width - parent.paddingRight
        canvas.clipRect(left, parent.paddingTop, right,
                parent.height - parent.paddingBottom)
    } else {
        left = 0
        right = parent.width
    }

    val childCount = parent.childCount
    for (i in 0 until childCount - 1) {
        val child = parent.getChildAt(i)
        parent.getDecoratedBoundsWithMargins(child, mBounds)
        val bottom = mBounds.bottom + Math.round(child.translationY)
        val top = bottom - mDivider!!.intrinsicHeight
        mDivider!!.setBounds(left, top, right, bottom)
        mDivider!!.draw(canvas)
    }
    canvas.restore()
}

private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
    canvas.save()
    val top: Int
    val bottom: Int

    if (parent.clipToPadding) {
        top = parent.paddingTop
        bottom = parent.height - parent.paddingBottom
        canvas.clipRect(parent.paddingLeft, top,
                parent.width - parent.paddingRight, bottom)
    } else {
        top = 0
        bottom = parent.height
    }

    val childCount = parent.childCount
    for (i in 0 until childCount - 1) {
        val child = parent.getChildAt(i)
        parent.layoutManager.getDecoratedBoundsWithMargins(child, mBounds)
        val right = mBounds.right + Math.round(child.translationX)
        val left = right - mDivider!!.intrinsicWidth
        mDivider!!.setBounds(left, top, right, bottom)
        mDivider!!.draw(canvas)
    }
    canvas.restore()
}
Mohsen Mirhoseini
  • 8,454
  • 5
  • 34
  • 59
0

If you have an id property in your object: List<class> then the last divider can easily be removed with data binding by comparing the id with the lastIndex of the list if the id is set in accordance with list indexes.

In your ViewModel:

var lastIndexOfList get() = List.lastIndex

Divider XML:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" >

    <data>
        <import type="android.view.View" />
        <variable
            name="Item"
            type="com.example.appName.Item" />
        <variable
            name="viewModel"
            type="com.example.appName.ViewModel" />
    </data>

    ...

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@android:color/darker_gray"
        android:visibility="@{ item.id == viewModel.lastIndexOfList ? View.GONE : View.VISIBLE }" />

    ...

</layout>
Ronnie
  • 207
  • 4
  • 11
0

The answer of Bhuvanesh BS is working but in my case spaces between items were ignored and divider showed to the top.

So, I want to share a (Kotlin) solution with space support.

class DividerDecoratorWithoutLastLine(
    private val dividerDrawable: Drawable,
    private val marginVerticalPx: Int = 0,
    private val marginHorizontalPx: Int = 0
) : RecyclerView.ItemDecoration() {

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        with(outRect) {
            right = marginHorizontalPx
            left = marginHorizontalPx

            top = marginVerticalPx
            bottom = marginVerticalPx
        }
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val dividerLeft = parent.paddingLeft
        val dividerRight = parent.width - parent.paddingRight
        val childCount = parent.childCount
        for (i in 0..childCount - 2) {
            val child: View = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val dividerTop: Int =
                child.bottom + params.bottomMargin + marginVerticalPx
            val dividerBottom = dividerTop + dividerDrawable.intrinsicHeight
            dividerDrawable.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom)
            dividerDrawable.draw(c)
        }
    }
}
LuzLaura98
  • 41
  • 6