You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET MAUI中绘制带填充内饼的环形进度条

环形进度条内部填充区域实现问题

我需要创建带有外部进度环、内部填充饼状区域(浅灰色)和中心百分比文本的环形进度条。

目前已实现的代码如下:

public void Draw(ICanvas canvas, RectF dirtyRect)
{
    float effectiveSize = Size - Thickness;
    float x = Thickness / 2;
    float y = Thickness / 2;

    if (Progress < 0)
    {
        Progress = 0;
    }
    else if (Progress > 100)
    {
        Progress = 100;
    }

    if (Progress < 100)
    {
        float angle = GetAngle(Progress);

        canvas.StrokeColor = ProgressLeftColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);

        // Draw arc
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawArc(x, y, effectiveSize, effectiveSize, 90, angle, true, false);
    }
    else
    {
        // Draw circle
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);
    }

    float fontSize = effectiveSize / 2.86f;
    canvas.FontSize = fontSize;
    canvas.FontColor = TextColor;

    float verticalPosition = ((Size / 2) - (fontSize / 2)) * 1.15f;
    canvas.DrawString($"{Progress}%", x, verticalPosition, effectiveSize, effectiveSize / 4, HorizontalAlignment.Center, VerticalAlignment.Center);
}

这段代码可以绘制带进度弧的环形及进度百分比文本,但我无法实现内部填充的饼状部分(浅灰色区域)。我尝试通过绘制两条直线加弧的方式填充,思路如下:

canvas.FillColor = Colors.Teal;

var center = new Point(effectiveSize / 2, effectiveSize / 2);
var startPoint = new Point(x, y);
var endPoint = new Point();
canvas.DrawLine(center, startPoint);
canvas.DrawLine(center, endPoint);
canvas.DrawArc((float)startPoint.X, (float)startPoint.Y,(float)endPoint.X, (float)endPoint.Y, 0, angle, true, false);

但我不知道如何计算端点,请问有没有更好的解决方案?


解决方案

要实现内部填充的饼状区域,最可靠的方式是使用**路径(Path)**构建封闭图形后填充,比单独绘制直线和弧更简洁准确。以下是具体实现:

修改后的完整代码

public void Draw(ICanvas canvas, RectF dirtyRect)
{
    float effectiveSize = Size - Thickness;
    float x = Thickness / 2;
    float y = Thickness / 2;
    // 环形中心坐标
    float centerX = x + effectiveSize / 2;
    float centerY = y + effectiveSize / 2;
    // 内部填充区域的半径
    float fillRadius = effectiveSize / 2;

    if (Progress < 0)
    {
        Progress = 0;
    }
    else if (Progress > 100)
    {
        Progress = 100;
    }

    // 先绘制内部填充饼状区域
    if (Progress > 0)
    {
        float angle = GetAngle(Progress);
        if (Progress == 100)
        {
            // 进度满值时直接绘制完整填充圆
            canvas.FillColor = FillColor; // 自定义浅灰色
            canvas.FillCircle(centerX, centerY, fillRadius);
        }
        else
        {
            // 用路径构建封闭填充区域
            using var path = new PathF();
            // 移动到环形中心
            path.MoveTo(centerX, centerY);
            
            // 计算弧的起始点(对应外部进度弧的90度起始位置)
            float startAngleRad = (float)(Math.PI * 1.5);
            float startX = centerX + (float)Math.Cos(startAngleRad) * fillRadius;
            float startY = centerY + (float)Math.Sin(startAngleRad) * fillRadius;
            path.LineTo(startX, startY);
            
            // 添加进度弧
            path.AddArc(x, y, effectiveSize, effectiveSize, 90, angle);
            
            // 计算弧的终点并连线回中心
            float endAngleRad = startAngleRad + (float)(Math.PI * 2 * (angle / 360));
            float endX = centerX + (float)Math.Cos(endAngleRad) * fillRadius;
            float endY = centerY + (float)Math.Sin(endAngleRad) * fillRadius;
            path.LineTo(endX, endY);
            path.Close(); // 自动闭合路径
            
            // 填充路径
            canvas.FillColor = FillColor;
            canvas.FillPath(path);
        }
    }

    // 绘制外部进度环(保留原有逻辑)
    if (Progress < 100)
    {
        float angle = GetAngle(Progress);

        canvas.StrokeColor = ProgressLeftColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);

        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawArc(x, y, effectiveSize, effectiveSize, 90, angle, true, false);
    }
    else
    {
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);
    }

    // 绘制中心文本(保留原有逻辑)
    float fontSize = effectiveSize / 2.86f;
    canvas.FontSize = fontSize;
    canvas.FontColor = TextColor;

    float verticalPosition = ((Size / 2) - (fontSize / 2)) * 1.15f;
    canvas.DrawString($"{Progress}%", x, verticalPosition, effectiveSize, effectiveSize / 4, HorizontalAlignment.Center, VerticalAlignment.Center);
}

// 进度转角度的方法
private float GetAngle(float progress)
{
    return progress * 3.6f; // 0-100进度对应0-360度
}

关键说明

  • 用PathF构建封闭区域:通过MoveTo定位中心,LineTo连接弧的起点,AddArc添加进度弧,最后LineTo回中心并Close路径,确保区域封闭可填充。
  • 角度坐标转换:将DrawArc的90度起始位置转换为数学弧度,保证坐标计算和外部进度弧完全对齐。
  • 边界处理:进度100%时直接绘制完整填充圆,避免冗余的路径构建操作。

内容的提问来源于stack exchange,提问作者Wasyster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:38:10