RecyclerView在最后一项之后删除分隔符/装饰器

Slo*_*vić 44 android divider android-recyclerview

我有一个非常简单的RecyclerView.这就是我设置分隔符的方法

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

这是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>
Run Code Online (Sandbox Code Playgroud)

问题是由于某些愚蠢的原因,分隔符不仅仅是在项目之间创建的.但也是在最后一项之后.而且我只想在每个项目之后的项目之间.

知道如何防止分隔符在最后一项之后显示吗?

干杯谢谢

Bhu*_* BS 73

试试这个代码,它不会显示最后一项的分隔符.

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

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

    @Override
    public void onDraw(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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

像这样设置你的分频器:

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(ContextCompat.getDrawable(context, R.drawable.divider));
recyclerView.addItemDecoration(dividerItemDecoration);
Run Code Online (Sandbox Code Playgroud)

  • Upvoted,但不应该是**i <= childCount - 2**或**i <childCount - 1**? (3认同)
  • 当然。通过在ondraw方法中调整dividerLeft和dividerRight的值 (2认同)
  • 该解决方案不起作用,但“onDrawOver”而不是“onDraw”完成了这项工作 (2认同)

Mak*_*aev 43

正如这里提出的,您可以像这样扩展DividerItemDecoration:

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 == parent.getAdapter().getItemCount() - 1) {
                outRect.setEmpty();
            } else {
                super.getItemOffsets(outRect, view, parent, state);
            }
        }
    }
);
Run Code Online (Sandbox Code Playgroud)

[更新]
@Rebecca Hsieh指出:

工作时RecyclerView您的项目视图没有透明背景,例如,

<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>
Run Code Online (Sandbox Code Playgroud)

RecyclerView调用DividerItemDecoration.getItemOffsets来测量子位置.此解决方案将最后一个分隔符放在最后一个项目后面.因此,RecyclerView中的项目视图应该有一个背景来覆盖最后一个分隔符,这使它看起来像隐藏.

  • 不知道为什么,但outRect.setEmpty()对我不起作用. (5认同)
  • @MaksimTuraev我不理解您的代码。如果.setEmpty()将Rect设置为0,0,0,0,那么为什么它只能在非透明背景下工作? (2认同)

Mil*_*aji 16

最简单的方法: 使用MaterialDividerItemDecoration并设置isLastItemDecoratedfalse


sma*_*871 11

这是已接受答案的Kotlin 版本:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


sey*_*gin 8

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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松使用它:

recyclerView.addItemDecorationWithoutLastDivider()
Run Code Online (Sandbox Code Playgroud)

  • `outRect.setEmpty()` 不会为我删除分隔线 (12认同)

Abd*_*Ali 6

接受的答案不会为装饰分配空间,因为它不会覆盖 getItemOffsets()

我从支持库调整了DividerItemDecoration,以排除最后一项的装饰

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

要应用装饰器,请使用

RecyclerView.ItemDecoration dividerItemDecoration = new DividerItemDecorator(dividerDrawable);
recyclerView.addItemDecoration(dividerItemDecoration);
Run Code Online (Sandbox Code Playgroud)

包含方向的来源可以在这里找到 https://gist.github.com/abdulalin/146f8ca42aa8322692b15663b8d508ff


Koz*_*nik 6

虽然这里的许多答案已经足够有帮助,但直到 Google 的MaterialDividerItemDecoration库来提供帮助。使用这个库,您甚至不需要实现自定义类来控制最后一项的插图和绘图等属性。您可以快速展示:

// 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
Run Code Online (Sandbox Code Playgroud)


Cyr*_*obé 5

Kotlin 版本并更新了AbdulAli 工作答案的原始 DividerItemDecorator 类的新签名函数:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


Cqr*_*ret 5

我基于DividerItemDecoration添加了对垂直和水平方向(在 Kotlin 中)的支持,受到本线程之前的一些答案的启发:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

val dividerItemDecoration = CustomDividerItemDecorator(
            ContextCompat.getDrawable(requireContext(), R.drawable.<DRAWABLE NAME>)!!,
            DividerItemDecoration.HORIZONTAL
    )
recyclerView.addItemDecoration(dividerItemDecoration)
Run Code Online (Sandbox Code Playgroud)


Sho*_*sar 5

这是一个 Kotlin 扩展类:

    fun RecyclerView.addItemDecorationWithoutLastItem() {

    if (layoutManager !is LinearLayoutManager)
        return

    addItemDecoration(DividerItemDecorator(context))
 }
Run Code Online (Sandbox Code Playgroud)

这是 DividerItemDecorator 类

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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>
Run Code Online (Sandbox Code Playgroud)

最后这样称呼它

recyclerView.addItemDecorationWithoutLastItem()
Run Code Online (Sandbox Code Playgroud)