ChatGPT解决这个技术问题 Extra ChatGPT

如何在 RecyclerView 中的项目之间添加分隔符和空格

这是以前在 ListView 类中如何使用 dividerdividerHeight 参数完成的示例:

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

但是,我在 RecyclerView 类中看不到这种可能性。

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

在这种情况下,是否可以定义边距和/或将自定义分隔线视图直接添加到列表项的布局中,还是有更好的方法来实现我的目标?

@EyesClear 添加项目 另一个 xml 并在列表相同的活动中使用它。
支持库 com.homeretailgroup.argos.android.view.decorators.DividerItemDecoration 中有一个类并像这样使用它:mRecyclerView.addItemDecoration(new DividerItemDecoration(activity, LinearLayoutManager.VERTICAL));
您可以为垂直列表的列表项添加底部边距,也许它可以用作分隔符?
最简单的方法是在适配器行中的第一项周围添加顶部/底部边距。安卓:layout_marginBottom="4dp"。 (注意将边距添加到父布局不会削减它。)

P
Peter Mortensen

2016 年 10 月更新

Android 支持库 25.0.0 版引入了 DividerItemDecoration 类:

DividerItemDecoration 是一个 RecyclerView.ItemDecoration,可用作 LinearLayoutManager 的项目之间的分隔符。它支持水平和垂直方向。

用法:

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

上一个答案

有些答案要么使用已被弃用的方法,要么没有给出完整的解决方案,所以我尝试做一个简短的、最新的总结。

ListView 不同,RecyclerView 类没有任何与分隔符相关的参数。相反,您需要扩展 ItemDecoration,即 RecyclerView 的内部类:

ItemDecoration 允许应用程序向适配器数据集中的特定项目视图添加特殊的绘图和布局偏移。这对于在项目、突出显示、视觉分组边界等之间绘制分隔线很有用。所有 ItemDecoration 都按照它们添加的顺序绘制,在项目视图之前(在 onDraw() 中)和在项目之后(在 onDrawOver(Canvas, RecyclerView, RecyclerView.State) 中。

垂直间距ItemDecoration

扩展 ItemDecoration,添加以空格 height 作为参数的自定义构造函数并覆盖 getItemOffsets() 方法:

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 (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = verticalSpaceHeight;
}

注意:您还可以修改 outRect.topoutRect.leftoutRect.right 属性以获得所需的效果。

分隔物品装饰

扩展 ItemDecoration 并覆盖 onDraw() 方法:

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

您可以调用第一个使用默认 Android 分隔符属性的构造函数,也可以调用第二个使用您自己的可绘制对象的构造函数,例如 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>

注意:如果您希望将分隔线覆盖您的项目,请改用 onDrawOver() 方法。

用法

要使用您的新类,请将 VerticalSpaceItemDecorationDividerSpaceItemDecoration 添加到 RecyclerView,例如在片段的 onCreateView() 方法中:

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;
}

还有 Lucas Rocha's library 应该简化项目装饰过程。我还没有尝试过。

它的 features 包括:

库存物品装饰品的集合,包括:

项目间距水平/垂直分隔线。

项目清单


@droppin_science 如果我错了,请纠正我,但我没有在 onDraw() 中创建任何对象。我只是引用已经存在的实例。
我想知道使用 Paint 而不是创建可绘制对象是否是个好主意?那么我可以在 onDrawOver 中调用 canvas.drawLine(startX, startY, stopX, stopY, mPaint) 吗?有什么性能差异吗?
只是一个信息性评论:如果您打算稍后在列表中添加项目,请始终将空间添加到最后一项。如果您不这样做,则在添加项目时,它将没有空间。感谢垂直空间!
如果项目完全不透明,如上所示的 DividerItemDecoration 将不起作用,分隔线将被项目透支。在这种情况下,您也需要覆盖 getItemOffsets() 并将底部偏移量添加到 outRect 以便分隔线最终位于项目之外。或者,您可以覆盖 onDrawOver() 而不是 onDraw() 以在项目之后绘制分隔线。
只需将分隔符添加到 recyclerView 的一整页代码是最好的答案。不好意思,谷歌。
L
Leonid Ustenko

只需添加

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

此外,您可能需要添加依赖项
implementation 'com.android.support:recyclerview-v7:28.0.0'

为了稍微自定义它,您可以添加一个自定义可绘制对象:

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

您可以自由使用任何自定义可绘制对象,例如:

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

不需要活动。上下文就足够了
这一定是正确答案。 Plz,将 getActivity 更改为上下文。
此外,最好从您的 LayoutManager 获取方向。
谢谢!您也可以使用 Configuration 作为垂直分隔符:if (orientation == Configuration.ORIENTATION_LANDSCAPE) { recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL)); } else { recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));}
不错的答案,但它还在最后一项之后添加了一个分隔符。
P
Peter Mortensen

能否请您注意 Alex Fu 在 GitHub 上的这个特定文件:https://gist.github.com/alexfu/0f464fc3742f134ccd1e

这是“直接从支持演示中提取”的 DividerItemDecoration.java 示例文件。(https://plus.google.com/103498612790395592106/posts/VVEB3m7NkSS

在我的项目中导入此文件并将其作为项目装饰添加到回收站视图后,我能够很好地获得分隔线。

这是我的 onCreateView 在包含 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;
}

我确信可以做额外的样式,但这是一个起点。 :)


你如何添加替换那些:“footerDividersEnabled”、“headerDividersEnabled”、“listSelector”、“fastScrollEnabled”、“smoothScrollbar”、“textFilterEnabled”?
关于如何放置样式的任何输入?
要设置此解决方案的样式,您需要覆盖主题中的“android:listDivider”属性
Divider 不适用于 RecyclerView。您需要使用 RecyclerView.itemDecoration。看到这个答案:stackoverflow.com/a/27664023/2311451
为什么分隔线会扩大项目的整个宽度?如何在规格中显示google.com/design/spec/components/lists.html#lists-specs
P
Peter Mortensen

一个简单的 ItemDecoration 实现,用于所有项目之间的相等空间:

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;
        }
    }
}

我得到了空间,但我如何得到一个分隔线
getChildPosition 现在已弃用,可以改用 getChildAdapterPosition
不要忘记(像我一样)删除对 super.getItemOffsets 的调用,否则您的偏移量将被覆盖。
@EyesClear 不应该使用 getChildLayoutPosition 吗?
这是否实现了以像素为单位的间距?
P
Peter Mortensen

一种简单的方法是为 RecyclerView 设置背景颜色,并为项目设置不同的背景颜色。这是一个例子......

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

以及底部边距为“x”dp 或 px 的 TextView 项目(尽管它可以是任何东西)。

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

输出...

https://i.stack.imgur.com/hNJFG.png


什么诡计!只需要在加载时保持列表白色。
谨防透支!
@shem 你能详细说明一下吗?
当在 Android 中绘制一层层之上(活动背景、回收视图背景和项目视图背景)时,Android 将它们全部绘制出来,以及对用户不可见的那些。这称为过度绘制,可能会影响您的性能,请在此处了解更多信息:youtube.com/watch?v=T52v50r-JfE
P
Peter Mortensen

这很简单,你不需要这么复杂的代码:

DividerItemDecoration divider =
    new DividerItemDecoration(mRVMovieReview.getContext(),
                              DividerItemDecoration.VERTICAL);

divider.setDrawable(ContextCompat.getDrawable(getBaseContext(),
                                              R.drawable.line_divider));

mRVMovieReview.addItemDecoration(divider);

在你的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>

W
WindRider

我处理 Divider 视图和 Divider Insets 的方式是添加 RecyclerView 扩展。

1.

通过命名 View 或 RecyclerView 添加新的扩展文件:

RecyclerViewExtension.kt

并在 RecyclerViewExtension.kt 文件中添加 setDivider 扩展方法。

/*
* 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.

drawable 包内创建一个可绘制资源文件,如 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>

您可以在其中指定 android:insetLeftandroid:insetRight 的左右 margin

3.

在初始化 RecyclerView 的 Activity 或 Fragment 上,您可以通过调用设置自定义可绘制对象:

recyclerView.setDivider(R.drawable.recycler_view_divider)

4.

干杯🍺

https://i.stack.imgur.com/jStGN.png


R
Rohit Sharma

正如我设置的 ItemAnimatorsItemDecorator 不随动画一起进入或退出。

我只是在每个项目的项目视图布局文件中都有一个视图行。它解决了我的情况。 DividerItemDecoration 觉得对于一个简单的分隔线来说太巫术了。

<View
    android:layout_width="match_parent"
    android:layout_height="1px"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:background="@color/lt_gray"/>

你说的对。动画不适用于 ItemDecoration。我不知道为什么,但没有我指定任何内容,我会得到动画,并且我发现 ItemDecoration 创建的线条没有遵循非常令人分心和丑陋。所以我会使用像你这样的解决方案。
你是如何处理最后一个项目的?
@oldergod 。你指出了正确的痛点。我首先会同意设计在最后一项上也有分隔符。但如果你不想那样。如果位置是最后一个,则为此视图分配一个 id 并隐藏在 bindView 中。
@Javanator 我明白了,我采用的方法相同。谢谢。
最简单的就是最好的
P
Peter Mortensen

我认为使用一个简单的分隔线会帮助你

为每个项目添加分隔符:

1.将这个添加到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:width="1dp"
    android:height="1dp" />
<solid android:color="#999999" />
</shape>

2. 创建 SimpleDividerItemDecoration 类

我用这个例子来定义这个类:

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. 在使用 RecyclerView 的 Activity 或 Fragment 中,在 onCreateView 内添加:

@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.添加Item之间的间距

您只需要将填充属性添加到您的项目视图

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

如何使它适用于 GridLayoutManager ,也可以显示单元格之间的垂直分隔线?
resources.getDrawable() 现在已弃用。您可以传入上下文并使用 ContextCompat.getDrawable(context, R.drawable.line_divider)
P
Praveen Singh

如果有人只想在项目之间添加 10 dp 间距,您可以通过将 drawable 设置为 DividerItemDecoration 来实现:

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

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

recyclerView.addItemDecoration(dividerItemDecoration);

其中 divider_10dp 是一个可绘制资源,其中包含:

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

P
Peter Mortensen

由于没有正确的方法来正确使用 Material Design 来实现这一点,我只是做了以下技巧来直接在列表项上添加一个分隔符:

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@color/dividerColor"/>

DividerItemDecoration 在我获得一些材料设计的高程信息后停止工作(以获得与收件箱中相同的效果);它对于一件简单的事情来说太复杂了。该解决方案简单且有效。
P
Peter Mortensen

您可以通过编程方式创建它,而不是创建用于更改分隔线高度和颜色的 shape xml

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)

这是有用的答案
P
Peter Mortensen

2016 年 10 月更新

有了支持库 v25.0.0,终于有了基本水平和垂直分隔线的默认实现!

DividerItemDecoration


P
Peter Mortensen

为您的视图添加边距。它对我有用。

android:layout_marginTop="10dp"

如果您只想添加 等间距 并希望在 XML 中添加,只需将 padding 设置为您的 RecyclerView 并将等量的 layoutMargin 设置为项目你充气到你的 RecyclerView,让背景颜色决定间距颜色。


虽然这会起作用,但这不是正确的答案,例如,因为如果不对行布局做额外的事情,这并不能解决问题,而且,在顶部会出现边距 x1,在行之间会出现边距 x2。
这不是一个好主意,因为在将填充应用于 RecyclerView 时,拉到列表末尾时的 overscroll 效果将应用不必要的填充
最好将项目的布局包装在支持库 CardView 中,这样您就可以控制其他属性,例如高程/阴影等:<?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>
P
Peter Mortensen

对于那些仅在 RecyclerView 中寻找 项目之间的空格 的人,请参阅我的方法,在该方法中,您可以在所有项目之间获得相等的空间,但在第一个和最后一个项目中我提供了更大的填充。我只在水平 LayoutManager 中向左/向右应用填充,在垂直 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.");
        }
    }
}

文件尺寸.xml

<resources>
    <dimen name="paddingItemDecorationDefault">10dp</dimen>
    <dimen name="paddingItemDecorationEdge">20dp</dimen>
</resources>

P
Peter Mortensen

这是添加分隔线的简单技巧

只需为您的回收站项目的布局添加一个背景,如下所示 < ImageView android:id="@+id/imageViewContactLogo" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginRight="10dp" android:src="@drawable/ic_user" />

K
Kevin Grant

这实际上并不能解决问题,但作为一种临时解决方法,您可以在 XML 布局中设置卡片上的 useCompatPadding 属性,使其测量值与在 Lollipop 之前的版本上相同。

card_view:cardUseCompatPadding="true"

L
Learn OpenGL ES

我将 DividerItemDecoration 从一个较旧的 gist 中分叉出来,并对其进行了简化以适合我的用例,并且我还对其进行了修改,以按照在 ListView 中绘制分隔线的方式来绘制分隔线,包括在最后一个列表项之后的分隔线。这也将处理垂直 ItemAnimator 动画:

1)将此类添加到您的项目中:

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)将装饰器添加到您的 RecylerView:

recyclerView.addItemDecoration(new DividerItemDecoration(getActivity()));

正确,它适用于 LinearLayoutManager。您可以采用它背后的想法来适应 GridLayoutManager。
J
JohnnyLambada

我觉得需要一个不使用 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);

我非常喜欢这个答案,我用单表达式 Kotlin 答案重写了它:

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

这与@Nerdy 的原始答案相同,只是它将分隔线的高度设置为 24dp 而不是另一个视图高度的百分比。


W
Westy92

这是一个装饰,可让您设置项目之间的间距以及边缘的间距。这适用于 HORIZONTALVERTICAL 布局。

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
            }
        }
    }
}

用法:

recyclerView.addItemDecoration(LinearSpacingDecoration(itemSpacing = 10, edgeSpacing = 20))

P
Peter Mortensen

取自 Google 搜索,将此 ItemDecoration 添加到您的 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.");
        }
    }
}

这仅适用于 LinearLayoutManager。 GridLayoutManager 应该怎么做?
M
Micro

这个链接对我来说就像一个魅力:

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.");
        }
    }
}

然后在您的活动中:

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

或者,如果您使用的是片段:

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

这工作正常,但它不会在列表中的最后一项下显示分隔线。我需要这样:mShowFirstDivider = falsemShowLastDivider = true,但它不起作用。知道为什么吗?
这不能很好地处理 GridLayoutManager。
P
Peter Mortensen

我们可以使用附加到 recyclerview 的各种装饰器来装饰项目,例如 DividerItemDecoration:

只需使用以下...取自 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);
        }
    }
}

然后使用上面的如下:

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

这将在列表中的每个项目之间显示分隔符,如下所示:

https://i.stack.imgur.com/06d1F.png

对于那些正在寻找更多详细信息的人,可以查看本指南 Using the RecyclerView _ CodePath Android Cliffnotes

这里的一些答案建议使用边距,但问题是:

如果同时添加上边距和下边距,它们会出现在项目之间,并且它们会太大。如果只添加其中一个,则整个列表的顶部或底部都没有边距。如果在顶部添加一半距离,在底部添加一半,则外边距会太小。

因此,唯一美学上正确的解决方案是系统知道在哪里正确应用的分隔符:在项目之间,而不是在项目之上或之下。


这并未演示 DividerItemDecoration 代码的外观。
它是一个 AOSP 类,我为您挖掘了代码..... gist.githubusercontent.com/alexfu/0f464fc3742f134ccd1e/raw/…
效果不好:它不处理不同高度的行,也不显示网格的垂直分隔线
P
Peter Mortensen

如果要为项目添加相同的空间,最简单的方法是为 RecycleView 添加顶部+左填充和为卡片项添加右+底部边距。

文件尺寸.xml

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

文件 list_item.xml

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

文件列表.xml

<RecyclerView
 android:paddingLeft="@dimen/divider"
 android:paddingTop="@dimen/divider"
/>

P
Peter Mortensen

对于 GridLayoutManager,我使用这个:

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;
    }
}

这适用于您拥有的任何跨度计数。


关于顶部空间,您如何将其更改为也支持 FlexboxLayoutManager
P
Peter Mortensen

您可以轻松地以编程方式添加它。

如果您的布局管理器是线性布局,那么您可以使用:

DividerItemDecoration 是一个 RecyclerView.ItemDecoration,可用作 LinearLayoutManager 的项目之间的分隔符。它支持水平和垂直方向。

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

Source


c
cesards

为了在 RecylerView 中实现项目之间的间距,我们可以使用 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 :)
        }
    }
})

考虑到我粘贴的代码,有几点需要考虑:

你真的不需要调用 super.getItemOffsets 但我选择了,因为我想扩展基类定义的行为。如果库有更新在幕后做更多的逻辑,我们会错过它。

作为向 Rect 添加顶部间距的替代方法,您还可以添加底部间距,但是与获取适配器的最后一项相关的逻辑更复杂,因此这可能会稍微好一些。

我使用扩展属性将简单整数转换为 dps:8.dp。像这样的东西可能会起作用:

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()

P
Peter Mortensen

我在列表项中添加了一行,如下所示:

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

“1px”将画出细线。

如果要隐藏最后一行的分隔符,请在 onBindViewHolder 上为最后一个列表项使用 divider.setVisiblity(View.GONE);


我更喜欢这个,其他的太复杂了。
P
Peter Mortensen

其中一种方法是同时使用 cardview 和 recycler 视图。我们可以轻松添加效果,例如分隔线。示例:使用 RecyclerView 创建动态列表 另一种方法是将视图作为分隔符添加到回收器视图的 list_item_layout 中。 <查看 android:id="@+id/view1" android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/colorAccent" />


P
Peter Mortensen
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);
            }
        }
    }
}

这将在每个项目的顶部和底部(或左侧和右侧)添加空间。然后您可以将其设置为您的 recyclerView

recyclerView.addItemDecoration(new CommonItemSpaceDecoration(16));

文件大小Utils.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);
    }
}