.NET

10 jul, 2019

Xamarin Rocket #8 — Indicador de Progresso

Publicidade

Fala galera,

Tentando manter a periodicidade semanal chegamos novamente com uma dica rápida de Xamarin.Forms!

A ideia é ser um artigo bem rápido e todas as dicas vão ficar em um único repositório, se você perdeu algum veja as que ja saíram :

· Xamarin Rocket #1 — LineBreakMode

· Xamarin Rocket #2 — Alterando o espaço das linhas e colunas do Grid

· Xamarin Rocket #3 — Tela Cheia, ocultando a barra de status

· Xamarin Rocket #4 — Imagens em botões

· Xamarin Rocket #5 — Notificação ao alterar a conexão

· Xamarin Rocket #6 — Como utilizar o GridLayout

· Xamarin Rocket #7 — Gestos


Muitas pessoas ja viram aquele indicador de progresso em Aplicativos, principalmente de exercício ( No meu caso só vi mesmo, porque fazer exercício .. bem hahuahahuaa ) .

Hoje vou lhes mostrar um código bem legal, que precisei utilizar em um App e pode ser muito útil para vocês para gerar um indicador de progresso.

Para ajudar em nosso controle vamos utilizar o incrível SkiaSharp!

Se você não conhece o SkiaSharp :

O SkiaSharp é uma API de gráficos 2D multiplataforma para plataformas .NET ,baseada na Biblioteca de Gráficos Skia do Google. Ele fornece uma API 2D abrangente que pode ser usada em mobile, e desktop para renderizar imagens.

Em resumo com o SkiaSharp, poremos criar controles com Modelos 2D. Muito útil para exibir gráficos por exemplo 😉

O Microcharts que demonstrei nesse artigo utiliza o SkiaSharp como base.

Agora vamos utiliza-lo para nos ajudar e criar o Indicador.

Criando nosso Indicador

Vamos ao nuget Baixar o pacote SkiaSharp.Views.Forms e instalar no projeto compartilhado :

Vamos criar um Helper que vai nos ajudar a pegar os tamanhos das telas para o controle ficar correto :

using System;
using System.Diagnostics;
namespace XamarinRocket
{
    public class ProgressHelpers
    {
        // Reference Values(Standard Pixel 1 Device)
        private const float refHeight = 1080;//1677;
        private const float refWidth = 632;//940;

        // Derived Proportinate Values
        private float deviceHeight = 1; // Initializing to 1
        private float deviceWidth = 1;  // Initializing to 1


        public ProgressHelpers() { }


        public void SetDevice(int deviceHeight, int deviceWidth)
        {
            this.deviceHeight = deviceHeight;
            this.deviceWidth = deviceWidth;
        }


        public float GetFactoredValue(int value)
        {

            float refRatio = refHeight / refWidth;
            float devRatio = deviceHeight / deviceWidth;

            float factoredValue = value * (refRatio / devRatio);

            return factoredValue;
        }

        public float GetFactoredHeight(int value)
        {
            return (float)((value / refHeight) * deviceHeight);
        }


        public float GetFactoredWidth(int value)
        {
            return (float)((value / refWidth) * deviceWidth);
        }

        public int GetSweepAngle(int goal, int achieved)
        {
            int SweepAngle = 260;
            float factor = (float)achieved / goal;

            return (int)(SweepAngle * factor);

        }

    }
}

Agora vamos criar nossa pagina utilizando o namespace : xmlns:skia=”clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms” para utilizar o Skiasharp , SKCanvasView :

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinRocket.Views.XamarinRocket8Page" xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms">
    
   <Grid >
    <Grid.RowDefinitions>
        <RowDefinition Height="6*" />
        <RowDefinition Height="10*" />
    </Grid.RowDefinitions>

         <skia:SKCanvasView x:Name="canvas" 
                            PaintSurface="OnCanvasViewPaintSurfaceAsync" 
                            Grid.Row="0" 
                            BackgroundColor="#ecf0f1"/>

        
         <StackLayout Orientation="Vertical" Grid.Row="1" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="White"  >

            <StackLayout Orientation="Horizontal" HorizontalOptions="Center" BackgroundColor="White" Padding="0,5,10,5">
                 <Label Text="  Hoje"/>
                 <Switch x:Name="sw_listToggle" IsToggled="{Binding Path=Monitored, Mode=TwoWay}" 
                    Toggled="SwitchToggledAsync" />
                <Label Text="Mês"/>
            </StackLayout>
            
            <Slider x:Name="sweepAngleSlider"
                   ValueChanged="SliderValueChanged"
                   Minimum="0" Maximum="260"
                   IsVisible="true"/>
       </StackLayout>
        </Grid>
</ContentPage>

Nessa mesma pagina vamos adicionar um Switch e um Slider para manipular nosso controle.

Em seguida vamos implementar o backEnd ,para gerar o controle :

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;

namespace XamarinRocket.Views
{
    public partial class XamarinRocket8Page : ContentPage
    {
        SKPaintSurfaceEventArgs args;
        ProgressHelpers progressHelpers = new ProgressHelpers();
        int exercicioDiario = 20;
        int exercicioMensal = 340;
        int meta = 900;

        public XamarinRocket8Page()
        {
            InitializeComponent();

            InitiateProgressUpdate();
        }

        void OnCanvasViewPaintSurfaceAsync(object sender, SKPaintSurfaceEventArgs args1)

        {
            args = args1;
            DrawGaugeAsync();

        }

        async void SwitchToggledAsync(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            await InitiateProgressUpdate();
        }


        void SliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            if (canvas != null)
            {
                canvas.InvalidateSurface();
            }
        }

        async Task AnimateProgress(int progress)
        {
            sw_listToggle.IsEnabled = false;
            sweepAngleSlider.Value = 1;

            for (int i = 0; i < progress; i = i + 5)
            {
                sweepAngleSlider.Value = i;
                await Task.Delay(3);
            }

            sweepAngleSlider.Value = progress;
            sw_listToggle.IsEnabled = true;

        }

        async Task InitiateProgressUpdate()
        {
            if (sw_listToggle.IsToggled)
                await AnimateProgress(progressHelpers.GetSweepAngle(meta, exercicioMensal));
            else
                await AnimateProgress(progressHelpers.GetSweepAngle(meta / 30, exercicioDiario));
        }

        public void DrawGaugeAsync()
        {
            int uPadding = 150;
            int side = 500;
            int radialGaugeWidth = 25;

            int lineSize1 = 220;
            int lineSize2 = 70;
            int lineSize3 = 80;

            int lineHeight1 = 100;
            int lineHeight2 = 200;
            int lineHeight3 = 300;

            float startAngle = -220;
            float sweepAngle = 260;

            try
            {
            
                SKImageInfo info = args.Info;
                SKSurface surface = args.Surface;
                SKCanvas canvas = surface.Canvas;
                progressHelpers.SetDevice(info.Height, info.Width);
                canvas.Clear();

                float upperPading = progressHelpers.GetFactoredHeight(uPadding);

                int Xc = info.Width / 2;
                float Yc = progressHelpers.GetFactoredHeight(side);


                int X1 = (int)(Xc - Yc);
                int Y1 = (int)(Yc - Yc + upperPading);


                int X2 = (int)(Xc + Yc);
                int Y2 = (int)(Yc + Yc + upperPading);

                SKPaint paint1 = new SKPaint
                {
                    Style = SKPaintStyle.Stroke,
                    Color = Color.FromHex("#e0dfdf").ToSKColor(),                   
                    StrokeWidth = progressHelpers.GetFactoredWidth(radialGaugeWidth),
                    StrokeCap = SKStrokeCap.Round                                  
                };


                SKPaint paint2 = new SKPaint
                {
                    Style = SKPaintStyle.Stroke,
                    Color = Color.FromHex("#05c782").ToSKColor(),                   
                    StrokeWidth = progressHelpers.GetFactoredWidth(radialGaugeWidth), 
                    StrokeCap = SKStrokeCap.Round                                   
                };


                SKRect rect = new SKRect(X1, Y1, X2, Y2);

                SKPath path1 = new SKPath();
                path1.AddArc(rect, startAngle, sweepAngle);
                canvas.DrawPath(path1, paint1);

                SKPath path2 = new SKPath();
                path2.AddArc(rect, startAngle, (float)sweepAngleSlider.Value);
                canvas.DrawPath(path2, paint2);

                using (SKPaint skPaint = new SKPaint())
                {
                    skPaint.Style = SKPaintStyle.Fill;
                    skPaint.IsAntialias = true;
                    skPaint.Color = SKColor.Parse("#676a69");
                    skPaint.TextAlign = SKTextAlign.Center;
                    skPaint.TextSize = progressHelpers.GetFactoredHeight(lineSize1);
                    skPaint.Typeface = SKTypeface.FromFamilyName(
                                        "Arial",
                                        SKFontStyleWeight.Bold,
                                        SKFontStyleWidth.Normal,
                                        SKFontStyleSlant.Upright);
                                       
                    if (sw_listToggle.IsToggled)
                        canvas.DrawText(exercicioMensal + "", Xc, Yc + progressHelpers.GetFactoredHeight(lineHeight1), skPaint);
                    else
                        canvas.DrawText(exercicioDiario + "", Xc, Yc + progressHelpers.GetFactoredHeight(lineHeight1), skPaint);
                }

                using (SKPaint skPaint = new SKPaint())
                {
                    skPaint.Style = SKPaintStyle.Fill;
                    skPaint.IsAntialias = true;
                    skPaint.Color = SKColor.Parse("#676a69");
                    skPaint.TextAlign = SKTextAlign.Center;
                    skPaint.TextSize = progressHelpers.GetFactoredHeight(lineSize2);
                    canvas.DrawText("Minutos", Xc, Yc + progressHelpers.GetFactoredHeight(lineHeight2), skPaint);
                }

                using (SKPaint skPaint = new SKPaint())
                {
                    skPaint.Style = SKPaintStyle.Fill;
                    skPaint.IsAntialias = true;
                    skPaint.Color = SKColor.Parse("#e2797a");
                    skPaint.TextAlign = SKTextAlign.Center;
                    skPaint.TextSize = progressHelpers.GetFactoredHeight(lineSize3);

                    if (sw_listToggle.IsToggled)
                        canvas.DrawText("Meta " + meta + " Min", Xc, Yc + progressHelpers.GetFactoredHeight(lineHeight3), skPaint);
                    else
                    {
                        canvas.DrawText("Meta " + meta / 30 + " Min", Xc, Yc + progressHelpers.GetFactoredHeight(lineHeight3), skPaint);
                    }
                }

            }
            catch (Exception e)
            {
                Debug.WriteLine(e.StackTrace);
            }
        }

    }
}

O que estamos fazendo ? Estamos utilizando o SkiaSharp para gerar um desenho 2D de um controle de indicador. Apenas para demonstrar o switch indica se você quer ver o Progresso Diário ou Mensal e chama o método : InitiateProgressUpdate.

O método DrawGaugeAsync cria todos os vetores necessários para exibir o controle. Com isto é possível demonstrar a flexibilidade do Skiasharp, você pode customizar seu controle da forma que desejar.

Vamos rodar :

Skiasharp é muito poderoso, e com criatividade é possível criar controles incríveis como o acima 🙂

Caso queira baixar o código utilizado no Exemplo: Clique aqui.

Quer ver outros artigos sobre Xamarin ? Clique aqui.

Espero ter ajudado!

Aquele abraço!