使用RecyclerView.ItemDecoration自定义RecyclerView圆角滚动条

使用RecyclerView.ItemDecoration自定义RecyclerView圆角滚动条

其实RecyclerView自带滚动条,设置android:scrollbars="vertical"即可。想让其长期显示,设置android:fadeScrollbars="false"即可。但是滚动条的端点样式是矩形的,有时会不符合设计要求,这时可以自定义一个滚动条来实现设计要求。

依据

主要想法是利用当前页第一条可视条目的位置计算滚动条的位置,再由当前页可视条目数量与总列表数量的比例计算滚动条在RecyclerView内的高度。

步骤一

继承类RecyclerView.ItemDecoration并实现方法onDrawOver方法

步骤二:在onDrawOver方法内实现以下逻辑

  • 取得第一条条目的位置
  • 均分容器高度
  • 根据当前页显示的数量,得出滚动条要显示的高度
  • 绘制直线(滚动条)

代码如下:

package com.hanvon;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

public class Decoration extends RecyclerView.ItemDecoration {
    //抗锯齿 : Paint.ANTI_ALIAS_FLAG
    //闪烁时启动抖动 : Paint.DITHER_FLAG
    private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    private float strokeWidth = 2;
    private float scrollbarMargin = 0;

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        int childCount = parent.getChildCount();
        int itemCount = state.getItemCount();
        if (childCount == 0) return;
        Log.i("LayoutManager", "childCount: " + childCount + " itemCount=" + itemCount);
        
        paint.setStrokeWidth(1);
        paint.setColor(Color.RED);

        //绘制滚动条
        if (childCount == itemCount) return;//如果不超过两页,不用绘制滚动条

        //获取当前Page第一个条目的位置
        View childAt = parent.getChildAt(0);
        int position = parent.getChildLayoutPosition(childAt);

        //均分RecyclerView的高度,计算单条目录所占比重
        float itemHeight = (parent.getHeight() - strokeWidth) / itemCount;
        float half = strokeWidth / 2;

        int viewWidth = parent.getWidth();
        float x = viewWidth - scrollbarMargin - 1;//- half;//靠右绘制并显示右边距,所以要减掉边距和至少1线宽(当StrokeWidth=1时也能显示)
        paint.setStrokeWidth(strokeWidth);//线宽
        paint.setStrokeCap(Paint.Cap.ROUND);//圆角
        c.drawLine(x, position * itemHeight + half, x, (position + childCount) * itemHeight + half, paint);

        //测试辅助线
        /*paint.setTextSize(8f);
        paint.setStrokeWidth(1);
        c.drawLine(x - 4, 0, x - 4, parent.getHeight(), paint);
        for (int i1 = 0; i1 < itemCount + 2; i1++) {
            c.drawLine(0, i1 * itemHeight + half, viewWidth, i1 * itemHeight + half, paint);
            c.drawText("" + i1, viewWidth - 50, i1 * itemHeight - 8, paint);
        }*/
    }
}

要注意代码里面的float itemHeight = (parent.getHeight() - strokeWidth) / itemCount;计算减去了strokeWidth

原因是线端点样式设置成了paint.setStrokeCap(Paint.Cap.ROUND),所以预留了端点样式的绘制位置,如果预留的话,在边沿的位置端点会被截断而不显示。

步骤三,在RecyclerView上使用该ItemDecoration

rvList.addItemDecoration(new Decoration());

不足

  • 不能拖动滚动条
  • 因为是按条目个数算显示滚动条高度,当条目显示一半时,滚动条占比可能不精确,不过只是单纯显示,应该够用了
posted @ 2025-11-28 20:25  枫叶孤星  阅读(0)  评论(0)    收藏  举报