这是之前在ListView类中使用divider和dividerHeight参数实现的一个例子:

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

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


当前回答

RecyclerView没有为绘制列表分隔符提供一个简单的接口。但实际上它为我们提供了更灵活的 划分隔线的方法。

我们使用RecyclerView。用分隔线或任何你想要的东西来装饰RecyclerView的瓷砖。这也是为什么它被称为ItemDecoration。

如《材料设计指南》所述:

分隔符位于贴图的基线内。

所以如果你想遵循材料设计指南,你不需要额外的空间来画分隔线。把它们画在瓷砖上。然而,你有权利做任何你想做的事。所以我实现了一个让你能够设置嵌入以及在瓷砖上/下面绘制的功能。

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

你可以这样使用它:

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

我还写了一个关于如何实现ItemDecoration以及如何使用它们的演示应用程序。你可以查看我的GitHub存储库Dividers-For-RecyclerView。它有三个实现:

下隔板 叠加分隔器 插入分隔器

其他回答

我有一个非常简单的方法在RecyclerView中添加一个分隔符。使用自定义适配器修改回收器视图布局,然后与回收器视图项目一起添加带有背景色(将是分隔色)的线性布局,并添加1dp的高度(或根据您的要求)和宽度以匹配父级。

下面是一个示例代码。

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

请注意一下Alex Fu在GitHub上的这个文件: 链接

它是“直接从支持演示中提取”的diveritemdecoration .java示例文件。

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

下面是我的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;
}

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

为了在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 :)
        }
    }
})

对于我粘贴的代码,有几件事需要考虑:

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

RecyclerView和ListView有点不同。实际上,RecyclerView需要一个类似ListView的结构。例如,线性布局。LinearLayout有用于划分每个元素的参数。

在下面的代码中,我有一个RecyclerView由一个线性布局中的CardView对象组成,其中有一个“填充”,将在项目之间放置一些空间。把空间缩小,就能得到一条线。

下面是recyclerview_layout.xml文件中的Recycler视图:

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

这里是每个项目看起来像(它显示为分割由于android:填充在线性布局包围一切)在另一个文件: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>

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,添加一个自定义构造函数,将空格高度作为参数,并覆盖getitemoffset()方法:

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。前,outRect。left和outRect。正确的属性为期望的效果。

分配器ItemDecoration

扩展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 divider属性,也可以调用第二个构造函数使用你自己的drawable,例如drawable/divide .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()方法。

使用

要使用你的新类,添加VerticalSpaceItemDecoration或DividerSpaceItemDecoration到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的图书馆,旨在简化道具装饰过程。不过我还没试过。

其特点包括:

收藏的库存项目装饰,包括: 项目间距水平/垂直分隔。 列表项