Android RecyclerView万能分割线

本文介绍了一个自定义RecyclerView分割线的工具类,通过该工具类可以灵活地为RecyclerView添加分割线,支持不同方向的列表和多种样式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很久之前接触过RecyclerView,前段时间闲来无事用了下,结果发觉又被分割线伤了下。于是找了分代码,理解和完善,形成了适合我的一个个万能分割线工具类。

 

主要方法:

 

    /**
     * 看图说话:get Item Offsets,获得item的偏移量。此方法用来控制item的偏移
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

}

 

 

 

 

 

    /**
     * 绘制分割线
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
}

 

 

 

 

 

多的原理不说了,去其他网站搜下吧。

不难,多花不说,分享代码。

全部代码,宁包入住。

 

package com.duke.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
 * author: duke
 * version: 2.0
 * dateTime: 2020-03-11 19:30
 * description:
 */
public class TopicRecycleViewDivider extends RecyclerView.ItemDecoration {

    // 绘制分割线的画笔
    private Paint paint;
    // 如果是画笔绘制,记录分割线宽度或高度 px
    private int paintWidthPX = 5;

    // 如果需要绘制给定的 drawable
    private Drawable drawableDivider;

    // 用画笔绘制颜色,还是绘制特定的drawable
    private DrawType drawType;

    // 注意:列表的方向,非分割线的方向
    // LinearLayoutManager.HORIZONTAL 或 LinearLayoutManager.VERTICAL
    private int orientation = LinearLayoutManager.VERTICAL;

    // 是否需要忽略第一个 item 后面的分割线,有些需求需要
    private boolean isSkipFirstItemBelowLine = false;
    // 是否需要忽略最后一个 item 后面的分割线,大多数需求都需要
    private boolean isSkipLaseItemBelowLine = true;

    /**
     * 忽略 第一个 item 下方或右侧的分割线。个别需要需要
     *
     * @param skipFirstItemBelowLine 是否忽略
     * @return this
     */
    public TopicRecycleViewDivider skipFirstItemBelowLine(boolean skipFirstItemBelowLine) {
        isSkipFirstItemBelowLine = skipFirstItemBelowLine;
        return this;
    }

    /**
     * 忽略 最后一个 item 下方或右侧的分割线。一般都需要
     *
     * @param skipLaseItemBelowLine 是否忽略
     * @return this
     */
    public TopicRecycleViewDivider skipLaseItemBelowLine(boolean skipLaseItemBelowLine) {
        isSkipLaseItemBelowLine = skipLaseItemBelowLine;
        return this;
    }

    /**
     * 设置列表方向,非分割线的
     *
     * @param listOrientation 列表方向,<br/>
     *                        LinearLayoutManager.HORIZONTAL or LinearLayoutManager.VERTICAL <br/>
     * @return this
     */
    public TopicRecycleViewDivider setListOrientation(int listOrientation) {
        if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
            throw new IllegalArgumentException("Parameter of orientation is error. Please see LinearLayoutManager ...");
        }
        orientation = listOrientation;
        return this;
    }

    /**
     * 构造函数
     *
     * @param context    context
     * @param drawableId 分割线图片
     */
    public TopicRecycleViewDivider(Context context, int drawableId) {
        drawType = DrawType.USE_DRAWABLE;
        drawableDivider = ContextCompat.getDrawable(context, drawableId);
    }

    /**
     * 自定义分割线
     *
     * @param dividerHeightPX 分割线高度 px
     * @param dividerColorInt 分割线颜色
     */
    public TopicRecycleViewDivider(int dividerHeightPX, int dividerColorInt) {
        drawType = DrawType.USE_PAINT;
        paintWidthPX = dividerHeightPX;
        // 绘制纯颜色 (之一:可以绘制纯颜色)
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(dividerColorInt);
        paint.setStyle(Paint.Style.FILL);
    }


    /**
     * 看图说话:get Item Offsets,获得item的偏移量。此方法用来控制item的偏移
     *
     * @param outRect outRect 表示在 item 的 上、下、左、右 四周撑开的距离,默认值为 0
     * @param view    当前的 holder view
     * @param parent  recyclerView
     * @param state   state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        int index = parent.getChildAdapterPosition(view);
        // 忽略第一个 item 的分割线
        if (isSkipFirstItemBelowLine && index == 0) {
            return;
        }
        int totalSize = state.getItemCount();
        // 忽略最后一个 item 的分割线
        if (isSkipLaseItemBelowLine && index == totalSize - 1) {
            return;
        }

        /**
         * 列表的方向为横向,画分割线就是纵向的,需要确定的是 child 的右边偏移值
         * 留出空间画分割线
         */
        if (orientation == LinearLayoutManager.HORIZONTAL) {
            int x = 0;
            switch (drawType) {
                case USE_PAINT:
                    x = paintWidthPX;
                    break;
                case USE_DRAWABLE:
                    x = drawableDivider.getIntrinsicWidth();
                    break;
            }
            outRect.set(0, 0, x, 0);
        }

        /**
         * 列表的方向为纵向,画分割线就是横向的,需要确定的是 child 的下边偏移值
         * 留出空间画分割线
         */
        else if (this.orientation == LinearLayoutManager.VERTICAL) {
            int x = 0;
            switch (drawType) {
                case USE_PAINT:
                    x = paintWidthPX;
                    break;
                case USE_DRAWABLE:
                    x = drawableDivider.getIntrinsicHeight();
                    break;
            }
            outRect.set(0, 0, 0, x);
        }
    }

    /**
     * 绘制分割线
     *
     * @param c      canvas
     * @param parent paint
     * @param state  state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (orientation == LinearLayoutManager.VERTICAL) {
            // 列表是纵向的,需要绘制横向的分割线
            drawHorizontalLine(c, parent);
        } else {
            // 列表是横向的,需要绘制纵向的分割线
            drawVerticalLine(c, parent);
        }
    }

    /**
     * 绘制横向 item 分割线。左、上、右都是可计算的,下需要获取给定的高度值
     *
     * @param canvas canvas
     * @param parent paint
     */
    private void drawHorizontalLine(Canvas canvas, RecyclerView parent) {
        // 左边:到父容器的 left 内间距位置值
        final int left = parent.getPaddingLeft();
        // 右边:到父容器的 right 内间距位置值
        final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
        final int childSize = parent.getChildCount();
        // 循环绘制每条分割线
        for (int i = 0; i < childSize; i++) {
            if (isSkipFirstItemBelowLine && i == 0) {
                continue;
            }
            if (isSkipLaseItemBelowLine && i == childSize - 1) {
                continue;
            }
            final View child = parent.getChildAt(i);
            if (!(child.getLayoutParams() instanceof RecyclerView.LayoutParams)) {
                continue;
            }
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            // 上边:具体的某条分割线的上边以 child 的 (bottom + bottomMargin) 位置值
            final int top = child.getBottom() + layoutParams.bottomMargin;
            // 下边:根据类型判断
            int bottom;
            switch (drawType) {
                case USE_PAINT:
                    // 构造方法声明使用画笔绘制
                    // 下边:top 加上指定的高度
                    bottom = top + paintWidthPX;
                    canvas.drawRect(left, top, right, bottom, paint);
                    break;
                case USE_DRAWABLE:
                    // 构造方法声明使用 drawable
                    // 下边:top 加上指定的高度
                    bottom = top + drawableDivider.getIntrinsicHeight();
                    drawableDivider.setBounds(left, top, right, bottom);
                    drawableDivider.draw(canvas);
                    break;
            }
        }
    }

    /**
     * 绘制纵向 item 分割线。上、下、左都是可计算的,右侧需要获取给定的宽度值
     *
     * @param canvas canvas
     * @param parent parent
     */
    private void drawVerticalLine(Canvas canvas, RecyclerView parent) {
        // 上边:到父容器的 top 内间距位置值
        final int top = parent.getPaddingTop();
        // 下边:到父容器的 bottom 内间距位置值
        final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
        final int childSize = parent.getChildCount();
        // 循环绘制每条分割线
        for (int i = 0; i < childSize; i++) {
            if (isSkipFirstItemBelowLine && i == 0) {
                continue;
            }
            if (isSkipLaseItemBelowLine && i == childSize - 1) {
                continue;
            }
            final View child = parent.getChildAt(i);
            if (!(child.getLayoutParams() instanceof RecyclerView.LayoutParams)) {
                continue;
            }
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            // 左边:具体的某条分割线的左边以 child 的 (right + rightMargin) 位置值
            final int left = child.getRight() + layoutParams.rightMargin;
            // 右边:根据类型判断
            int right;
            switch (drawType) {
                case USE_PAINT:
                    // 构造方法声明使用画笔绘制
                    // 右边:left 加上指定的宽度
                    right = left + paintWidthPX;
                    canvas.drawRect(left, top, right, bottom, paint);
                    break;
                case USE_DRAWABLE:
                    // 构造方法声明使用 drawable
                    // 右边:left 加上指定的宽度
                    right = left + drawableDivider.getIntrinsicWidth();
                    drawableDivider.setBounds(left, top, right, bottom);
                    drawableDivider.draw(canvas);
                    break;
            }
        }
    }

    public enum DrawType {
        USE_PAINT(1),       // 用画笔绘制纯颜色
        USE_DRAWABLE(2);    // 绘制特定的 drawable

        private final int type;

        DrawType(int type) {
            this.type = type;
        }

        public int getType() {
            return type;
        }
    }
}

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值