Мне нужно создать индикатор выполнения, который выглядит так.
Что я сделал на данный момент:
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);
Как рассчитать конечную точку. Или есть лучшее решение.
спасибо





Вы можете попробовать нарисовать этот круг с помощью SkiaSharp. Я делюсь решением здесь. Для получения дополнительной информации вы можете обратиться к Кривые и траектории SkiaSharp. Это также работает для мауи.
Используйте ползунок для управления углом:
<VerticalStackLayout
Spacing = "25"
Padding = "30,0"
VerticalOptions = "Center">
<Slider Maximum = "360" ValueChanged = "Slider_ValueChanged" />
<skia:SKCanvasView x:Name = "mycanvas" WidthRequest = "400" HeightRequest = "400" PaintSurface = "SKCanvasView_PaintSurface"/>
</VerticalStackLayout>
В коде позади:
void Slider_ValueChanged(System.Object sender, Microsoft.Maui.Controls.ValueChangedEventArgs e)
{
if (e.NewValue >= 360)
{
// the value cannnot be equal to 360
SweepAngle = (float)359.99;
}
else
{
SweepAngle = (float)e.NewValue;
}
mycanvas.InvalidateSurface();
}
void SKCanvasView_PaintSurface(System.Object sender, SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs e)
{
SKImageInfo info = e.Info;
SKSurface surface = e.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// draw the default background circle ring
SKPaint cPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.LightGray,
StrokeWidth = 50
};
SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
canvas.DrawCircle(center, 200, cPaint);
cPaint.Color = SKColors.AliceBlue;
cPaint.Style = SKPaintStyle.Fill;
canvas.DrawCircle(center, 200, cPaint);
//draw the progress bar outer ring
SKRect rect = new SKRect(center.X-200, center.Y-200, center.X + 200, center.Y + 200);
SKPaint aPaint = new SKPaint()
{
StrokeWidth = 50,
Color = SKColors.Green,
IsStroke = true
};
canvas.DrawArc(rect, -90, SweepAngle, false,aPaint);
// draw the inner circle of the progress bar
using (SKPath path = new SKPath())
using (SKPaint fillPaint = new SKPaint())
using (SKPaint outlinePaint = new SKPaint())
{
SKRect rect1 = new SKRect(center.X - 200, center.Y - 200, center.X + 200, center.Y + 200);
path.MoveTo(e.Info.Width / 2, e.Info.Height / 2);
path.ArcTo(rect1, -90, SweepAngle, false);
fillPaint.Style = SKPaintStyle.Fill;
fillPaint.Color = SKColors.LightGreen;
canvas.DrawPath(path, fillPaint);
}
SKPaint textPaint = new SKPaint
{
Color = SKColors.LightSeaGreen,
StrokeWidth = 1,
};
// draw the text
string text = (Math.Round(SweepAngle/360*100,0)).ToString() + "%";
float textWidth = textPaint.MeasureText(text);
textPaint.TextSize = 0.2f * info.Width * textPaint.TextSize / textWidth;
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
canvas.DrawText(text, xText, yText, textPaint);
}
Надеюсь, поможет!
Вы можете добавить builder.UseSkiaSharp() в MauiProgram.cs
У вас уже есть хороший ответ с использованием Skia, но если вы хотите использовать Microsoft.Maui.Graphics, вот альтернатива. Нет текста, потому что даже если его можно добавить в рисуемый объект, лучше в ContentView, который инкапсулирует все это, если вы делаете элемент управления, то есть. Оттуда вы можете добавить BindableProperty и все подобные механизмы.
public class ProgressbarDrawable : IDrawable
{
public double Progress { get; set; } = 100;
public void Draw(ICanvas canvas, RectF dirtyRect)
{
canvas.SaveState();
var startAngle = 90;
var progressAngle = Progress / 100;
var endAngle = startAngle - (int)Math.Round(progressAngle * 360, MidpointRounding.AwayFromZero);
var isFullCircle = Progress == 100;
var centerX = dirtyRect.Width / 2;
var centerY = dirtyRect.Height / 2;
var radius = dirtyRect.Width / 2;
canvas.StrokeColor = new Color(243, 242, 238);
canvas.StrokeSize = 10;
canvas.DrawCircle(centerX, centerY, radius);
if (isFullCircle)
{
canvas.FillColor = new Color(235, 243, 231);
canvas.FillCircle(centerX, centerY, radius);
canvas.StrokeColor = new Color(132, 189, 137);
canvas.StrokeSize = 10;
canvas.DrawCircle(centerX, centerY, radius);
}
else
{
var path = new PathF();
path.MoveTo(centerX, centerY);
path.AddArc(centerX - radius, centerY - radius, radius * 2, radius * 2, startAngle, endAngle, true);
path.LineTo(centerX, centerY);
canvas.FillColor = new Color(235, 243, 231);
canvas.FillPath(path);
canvas.StrokeColor = new Color(132, 189, 137);
canvas.StrokeSize = 10;
canvas.DrawArc(centerX - radius, centerY - radius, radius * 2, radius * 2, startAngle, endAngle, true, false);
}
canvas.RestoreState();
}
}
Выполнение
<ContentPage.Resources>
<drawables:ProgressbarDrawable x:Key = "ProgressbarDrawable" />
</ContentPage.Resources>
<GraphicsView
x:Name = "ProgressbarDrawable"
Drawable = "{StaticResource ProgressbarDrawable}"
HeightRequest = "200"
WidthRequest = "200" />
Вероятно, будет проще, если вы построите геометрию пути как для внешней дуги, так и для внутреннего конуса. Первый также может дать вам последнюю точку пути. Это что-то вроде этого (правда, не MAUI, но концепция ~та же). Или вы можете рассчитать вращение самостоятельно; см. метод
RotatePoint()здесь: Многократное вращение точки вокруг точки вращения