如何在.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
相关产品推荐
相关产品推荐

