这是之前在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"/>

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


当前回答

•PrimeAdapter

通过使用PrimeAdapter,在RecyclerViews中处理分隔符可以非常简单。它为创建和管理分隔器提供了多种功能和更大的灵活性。

您可以按以下方式创建适配器(请参阅GitHub中关于使用的完整文档):

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

创建适配器实例后,您可以简单地使用以下命令添加分隔符:

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

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

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

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

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

其他回答

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中寻找项目之间的空间的人,请参阅我的方法,在所有项目之间获得相等的空间,除了在第一个和最后一个项目中,我给出了较大的填充。我只应用填充左/右在水平的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.");
        }
    }
}

文件dimens.xml

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

使用RecyclerView itemdecoration -一个基本的分隔符样本在Kotlin android

一个完整的示例,包括一个构建器,添加边缘或使用资源颜色的可能性

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

    private val mPaints = Paint()

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

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

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

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

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

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

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

            // get the position
            val position = params.viewAdapterPosition

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

设置很简单。只需将您的装饰与其余的初始设置一起添加到您的recyclerView。

 val decoration = SeparatorDecoration(context, R.color.primaryColor, 1.5f)
 recyclerview.addItemDecoration(decoration)

请注意一下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;
}

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

如果你想为物品添加相同的空间,最简单的方法是为RecycleView添加顶部+左侧填充,为卡片物品添加右侧+底部空白。

文件dimens.xml

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

文件list_item.xml

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

    ...
</CardView>

文件list.xml

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