Создание измерителя полноты (отображение статуса) в Xamarin

Я пытаюсь создать элемент управления, который отображает текущий статус процесса, как этот снимок ниже.

Контроль состояния

Итак, у нас есть круговое отображение статуса с цветными разделами для этапов или контрольных точек. На изображении мы уже прошли первые два этапа, а третий этап - на 70%.

Я знаю, что в JQuery есть элемент управления, который был очень похож. Но я не уверен, если в Xamarin Forms есть сторонний контроль, который я могу использовать. Если нет стороннего контроля, как мне следует продолжить разработку.

Должен ли я просто создавать изображения для разных этапов и отображать изображение? Или я должен создать настраиваемый элемент управления, который может принимать два значения: "веха" и "процент_комплект", а затем спроектировать, возможно, круговую диаграмму "на лету"?

Ответ 1

Используя NGraphics w/ NControl, вы можете создать "векторную" версию вашего "счетчика полноты" без создания рендеринга платформы или для добавления в проект таких библиотек, как Skia.

Примечание. SkiaSharp и другие родные библиотеки 2d/3d великолепны, но добавляют много накладных расходов в приложение, и если вам не нужны все их функции, тогда раздутие (размер приложения, использование памяти, время инициализации и т.д..) не стоит (ИМХО).

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

Я разделил MultiSegmentProgressControl, который я сделал, чтобы показать вам основы рисования дуги. Полная версия, которую я сделал, позволяет добавлять и анимировать несколько сегментов, отображать проценты, сегменты разбивки при касании и т.д.

Используя NControl, вы можете создавать составные элементы управления с сенсорными элементами, поэтому вам нужно знать, как далеко вы должны его принять.

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

введите описание изображения здесь

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)
        );
    }
}

При использовании NGraphics я настоятельно рекомендую использовать NGraphics.Editor или Xamarin WorkBook для интерактивного проектирования вашего контроля:

введите описание изображения здесь

Ответ 2

Если вы не можете найти уже выполненное решение (настоятельно рекомендуется, если вы можете его найти!), то создание элемента управления с добавочными вызовами в графическую библиотеку с вызовами DrawSegment может работать.

  • Нарисуйте сегмент для каждой части сильно окрашенного наружного кольца Разделы,
  • Нарисуйте сегмент с уменьшенным радиусом для внутреннего, тусклого цвета Разделы,
  • Нарисуйте белый круг для центральной белой панели.

Удачи!