为RecyclerView添加精美的分割线

前言

最近因为需求问题,须要写一个列表。之前老是使用ListView,历来没有用过RecyclerView。因此此次打算尝试一下。而后就开始动手干活了。RecyclerView布局写好了。而后写adapter,一切正常。
这是没有添加分割线的java

发现没有分割线有点难看。准备去布局文件里面写分割线,发现竟然没有divider这个属性。后来百度了一下,原来RecyclerView设置分割线是代码中添加的。android

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

如今Google已经内置了DividerItemDecoration类,之前是没有这个类的,须要咱们本身写,如今愈来愈方便了。第一个参数须要传入Context,第二个参数是分割线的类型,我这个是表示vertical。固然,若是你有须要你彻底能够设置为horizontal。你觉得这样就设置好分割线了吗?答案显而易见,没有。web

接下来,你须要在res目录下drawable的文件夹里面新建一个divider.xml文件。名字你能够按你本身的喜爱来设置。canvas

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@color/gray"/>
    <size android:height="0.1dp"/>
</shape>

我这里gray是#D0D0D0,你们也能够本身修改。app

再而后,你须要在values文件夹里面的styles文件里面添加这样一行代码。ide

<item name="android:listDivider">@drawable/divider</item>

好,写到这里。大功告成。先运行一下看看效果。
最后一行分割线没有去掉
嗯?忽然发现最后一行数据的下面也有分割线,这样不太美观。接下来咱们继续修改。svg

聪明的小伙伴可能已经知道怎么修改了。答案就是前面提到的DividerItemDecoration这个类。咱们进去看看。布局

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;

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

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

    @SuppressLint("NewApi")
    private void drawVertical(Canvas canvas, RecyclerView parent) {
        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; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    @SuppressLint("NewApi")
    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int top;
        final int bottom;
        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();
        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(ViewCompat.getTranslationX(child));
            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 (mOrientation == VERTICAL) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

里面有这样一个方法drawVertical,没错它就是在这个方法里面循环的绘制每一条分割线的。因此咱们只须要将for循环里面的i<childCount改成i<childCount-1就能够了。这里我用到的是垂直方向上的,因此drawHorizontal方法里面的数据我就没有修改。你们本身按需求修改哈。可是DividerItemDecoration这个类咱们可能不能直接修改,咱们能够新建一个类DividerItemsDecoration继承自RecyclerView.ItemDecoration,将DividerItemDecoration里面的代码copy过来,将我上面说的东西修改掉,这样就真的大功告成了。接下来运行咱们的程序,就能够看到精美的
分割线了。是否是很开心。this

添加好的分割线