Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating completeness meter (Status display) in Xamarin

I am trying to design a control that displays current status of a process, like this image below.

Status Control

So, we have a circular display of status with colored sections for milestones or checkpoints. In the image, we are already through the first two stages, and third stage is 70% done.

I know there is a control in Jquery that was pretty similar. But I am not sure, if there is a third party control in Xamarin Forms that I can use. If there is no third party control, how should I proceed with the design.

Should I just create images for different stages and display the image? Or should I create a custom control which can take two values, "milestone" and "percentage_complete", and then design maybe a pie chart on the fly?

like image 887
jitendragarg Avatar asked Nov 15 '17 08:11

jitendragarg


2 Answers

Using NGraphics w/ NControl you can create a "vector" version of your "completeness meter" without creating platform renderers or needing to add libraries like Skia to your project.

Note: SkiaSharp and other native 2d/3d libraries are great, but add an lot of overhead to an app and if you do not need all their features then the bloat (app size, memory usage, initialization time, etc...) is not worth it (IMHO).

re: https://github.com/praeclarum/NGraphics

I stripped down a MultiSegmentProgressControl that I did to show you the basics of the arc drawing. The full version that I did allows you to add and animate multiple segments, displaying percentages, break-out segments on touch, etc...

Using NControl you can create composite controls with touch elements, so it is up to you on how far you need to take it.

re: https://github.com/chrfalch/NControl

enter image description here

public class MultiSegmentProgressControl2 : NControlView
{
    double ringWidth = 50;
    double ringInnerWidth = 100;
    SolidBrush redBush = new SolidBrush(Colors.Red);
    RadialGradientBrush redSegmentBrush = new RadialGradientBrush(
            new Point(0.5, 0.5),
            new Size(.75, .75),
            Colors.LightGray,
            Colors.Red);
    SolidBrush blueBush = new SolidBrush(Colors.Blue);
    RadialGradientBrush blueSegmentBrush = new RadialGradientBrush(
            new Point(0.5, 0.5),
            new Size(.75, .75),
            Colors.LightGray,
            Colors.Green);

    Tuple<double, double> _redSegment;
    public Tuple<double, double> RedSegment { get { return _redSegment; } set { _redSegment = value; Invalidate(); } }
    Tuple<double, double> _greenSegment;
    public Tuple<double, double> GreenSegment { get { return _greenSegment; } set { _greenSegment = value; Invalidate(); } }

    public override void Draw(ICanvas canvas, Rect rect)
    {
        canvas.FillEllipse(rect.TopLeft, rect.Size, Colors.Gray);
        var n = rect;
        n.X += ringWidth;
        n.Y = n.X;
        n.Width -= ringWidth * 2;
        n.Height = n.Width;
        var i = n;
        canvas.FillEllipse(n.TopLeft, n.Size, Colors.LightGray);
        n.X += ringInnerWidth;
        n.Y = n.X;
        n.Width -= ringInnerWidth * 2;
        n.Height = n.Width;
        canvas.FillEllipse(n.TopLeft, n.Size, Colors.White);

        var r = rect.Width / 2;
        DrawSegment(canvas, rect, ringWidth, redBush, r, _redSegment.Item1, _redSegment.Item2);
        DrawSegment(canvas, i, ringInnerWidth, redSegmentBrush, r - ringWidth, _redSegment.Item1, _redSegment.Item2);
        DrawSegment(canvas, rect, ringWidth, blueBush, r, _greenSegment.Item1, _greenSegment.Item2);
        DrawSegment(canvas, i, ringInnerWidth, blueSegmentBrush, r - ringWidth, _greenSegment.Item1, _greenSegment.Item2);
    }

    void DrawSegment(ICanvas canvas, Rect rect, double width, Brush brush, double r, double s, double f)
    {
        canvas.DrawPath(new PathOp[]{
            new MoveTo(SegmentEdgePoint(rect.Center, r, s)),
            new ArcTo(new Size(rect.Height / 2, rect.Width / 2), false, true, SegmentEdgePoint(rect.Center, r, f)),
            new LineTo(SegmentEdgePoint(rect.Center, r - width, f)),
            new ArcTo(new Size(r, r), false, false, SegmentEdgePoint(rect.Center, r - width, s)),
            new LineTo(SegmentEdgePoint(rect.Center, r, s)),
            new ClosePath()
        }, null, brush);
    }

    Point SegmentEdgePoint(Point c, double r, double d)
    {
        return new Point(
            c.X + r * Math.Cos(d * Math.PI / 180),
            c.Y + r * Math.Sin(d * Math.PI / 180)
        );
    }
}

When using NGraphics I highly recommend using the NGraphics.Editoror a Xamarin' WorkBook to interactively design your control:

enter image description here

like image 158
SushiHangover Avatar answered Nov 17 '22 10:11

SushiHangover


If you can't find an already completed solution (highly recommended if you can find one!) then creating a control with incremental calls to a graphics library with DrawSegment calls might work.

  1. Draw a segment for each part of the strongly coloured outer ring sections,
  2. Draw a segment with reduced radius for the inner, dimly coloured sections,
  3. Draw a white circle for the centre white pad.

Good luck!

like image 25
Monza Avatar answered Nov 17 '22 10:11

Monza