重学 Android 自定义 View 系列(十):带指针的渐变环形进度条

前言

该篇文章根据前面 重学 Android 自定义 View 系列(六):环形进度条 拓展而来。

最终效果如下:
在这里插入图片描述

1. 扩展功能


  1. 支持进度顺时针或逆时针显示
  2. 在进度条末尾添加自定义指针图片
  3. 使用线性渐变为进度条添加颜色效果

2. 关键技术点解析


2.1 进度方向控制的实现

通过添加一个 direction 属性,设置角度的正负,决定进度条是顺时针还是逆时针绘制:

public static final int CLOCKWISE = 1;
public static final int COUNTERCLOCKWISE = -1;
private int direction = COUNTERCLOCKWISE; // 默认逆时针

// 在 onDraw 方法中计算扫过的角度时,加入方向
float sweepAngle = 360f * progress / maxProgress;
sweepAngle *= direction;

// 绘制进度条
canvas.drawArc(rectF, startAngle, sweepAngle, false, progressPaint);

2.2 自定义指针图片的绘制

在环形进度条的末尾,绘制一张 Bitmap 图片作为指针,图片可由你传入,但指针的方向要和demo中的一致,如下:

在这里插入图片描述

绘制指针的步骤:

  1. 调整指针的绘制半径:确保指针贴合圆环外侧,加入一个 outerSize 参数用于控制指针漏出圆环的长度。
  2. 计算指针位置:使用三角函数计算图片中心点坐标。
  3. 旋转画布并绘制图片(关键):将画布旋转到指定角度后,再绘制指针图片。

用到的三角函数原理如下,再重温一下学校的知识:),因为在Java中 Math 函数计算三角函数用的是弧度而不是角度,所以代码使用了Math.toRadians进行了角度转弧度。
在这里插入图片描述

核心代码实现:

private void drawPointer(Canvas canvas, float angle) {
   
   
    // 调整半径,使指针图片紧贴圆环外部
    float adjustedRadius = radius + backgroundPaint.getStrokeWidth() / 2 + outerSize;

    // 计算指针的中心点位置
    float rightCenterX = centerX + adjustedRadius * (float) Math.cos(Math.toRadians(angle));
    float rightCenterY = centerY + adjustedRadius * (float) Math.sin(Math.toRadians(angle));

    // 计算Bitmap左上角位置
    float left = rightCenterX - bitmapWidth;
    float top = rightCenterY - bitmapHeight / 2;

    // 保存画布状态,旋转画布
    canvas.save();
    canvas.rotate(angle, rightCenterX, rightCenterY);

    // 绘制指针Bitmap
    canvas.drawBitmap(pointerBitmap, left, top, null);

    // 恢复画布状态
    canvas.restore();
}

重点是角度的计算 angle = startAngle + sweepAngle,和指针的位移与旋转,结合三角函数计算坐标,并通过旋转画布保持图片对齐,使指针始终指向圆心位置。

2.3 渐变颜色的实现

为进度条添加线性渐变效果使用了 LinearGradient 着色器,实际效果按需求自定义,重点是 计算渐变起点和终点,因为有起始角度的存在,需要用到 圆心坐标、半径和起始角度计算:

private void updateGradient() {
   
   
    // 计算圆上的起点和终点坐标
    double startAngleRadians = Math.toRadians(startAngle);
    float startX = centerX + (float) (radius * Math.cos(startAngleRadians));
    float startY = centerY + (float) (radius * Math.sin(startAngleRadians));
    float endX = centerX - (float) (radius * Math.cos(startAngleRadians));
    float endY = centerY - (float) (radius * Math.sin(startAngleRadians));

    //线性渐变,从一个点渐变到另一个点,因为渐变的距离是圆的直径 所以,TileMode 在这里实际无意义
    gradientShader = new LinearGradient(
            startX, startY, endX, endY,
            progressColors, null,
            Shader.TileMode.CLAMP
    );
    progressPaint.setShader(gradientShader);
}

4. 定义自定义属性


<declare-styleable name="CircularProgressBarEx">
        <!-- 进度条的最大值 -->
        <attr name="maxProgress" format="integer"/>
        <!-- 当前进度 -->
        <attr name="progress" format="integer"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值