I need to create a progress bar that looks like this.
What I did for now is:
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);
}
This draws a nice circle with an arc. I am struggling with the inner pie part (light gray part). I need that part to be filled with some color.
One of my ideas was to create two lines then connect them with an arc, and fill the object. Something like this.
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);
How can I calculate the endpoint. Or is there any better solution.
thnx
You may try using SkiaSharp to draw this pie. I share a solution here. For more info, you may refer to SkiaSharp Curves and Paths. It also works for maui.
Use a slider to control the angle:
<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>
In code behind:
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);
}
Hope it helps!