GradientProgressBar: Aceptando TouchEvents en Skiasharp

Hola techies, este es otro post super veloz acerca de SkiaSharp y este control que hice con Skia.

Cuando publique el post acerca de SkiaSharp (puede revisar aqui) algunas personas me preguntaron si podia hacer que el progreso en la barra de progreso sea editable y se pudiera cambiar por el usuario, de tal forma que el usuario pueda tocar y actualizar el progreso. Así que hagámoslo para poder revisar como SkiaSharp maneja los touch events.

TL;DR;

Si prefieres revisar el código directamente, puedes hacerlo en el repositorio de Github https://github.com/jesulink2514/XamBooksApp/tree/feature/touch-progress

Empecemos!

Ok, para comenzar necesitamos agregarle un constructor a nuestro control y habilitar los touch events, facil.

public class GradientProgressBar: SKCanvasView
{
    public GradientProgressBar()
    {
        EnableTouchEvents = true;
    }
...

Para poder calcular el porcentaje basado en la posición del touch, necesitamos en primer lugar el factor de escala (scale factor) para poder convertir entre las unidades de Xamarin.Forms y las unidades de SkiaSharp (una clase de unidad como pixel) asi que voy a definir un campo (field) protegido llamado ScaleFactor;

protected float ScaleFactor;

Ya estábamos calculando este factor en nuestro método OnPaintSurface así que solo necesitamos fijar el valor en nuestro campo ScaleFactor.

protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
    var info = e.Info;
    var canvas = e.Surface.Canvas;

    float width = (float)Width;
    var scale = CanvasSize.Width / width;
    ScaleFactor = scale;
    
    ...

Con todo este setup en su lugar (que no ha sido mucho en realidad) , podemos iniciar la magia. Debemos sobrescribir el método OnTouch para poder procesar los eventos táctiles o touch events. Este método recibe una instancia de SKTouchEventArgs que nos da toda la información disponible acerca de la interacción.

Me interesa manejar los cambios mientras los usuarios estan presionando el progress bar, no solo al inicio o al final de la interaccion por eso vamos a manejar SKTouchAction.Pressed, SKTouchAction.Released y SKTouchAction.Moved. Tu puedes revisar mas acerca de las opciones disponibles en la enumeración aqui.

Entonces, debemos calcular el nuevo porcentaje en nuestro control considerando la posición donde el usuario esta tocando el control, para esto nuestra instancia de SKTouchEventArgs  nos provee la propiedad Location y dentro la propiedad X .

Si pensamos en la matemática aquí, si conocemos el ancho para 100% y tambien el valor actual de X, basado en la regla de 3, el porcentaje seria igual al valor de x divido por el ancho del control que representa el 100%.

Me gusta agregar este Math.Max para el porcentaje para evitar que los valores caigan debajo de cero y este Math.Min para el valor de  Location.X y con eso evitar que los valores vayan mas allá de 100%.

Y el toque final (haha toque, bien si lo entendiste), es  e.Handled = true, para evitar que el evento haga bubbling, es decir se propague.

Y ya esta, eso es todo, nuestro control ahora recibe input del usuario para fijar el porcentaje.

PD:  Puedes desactivar este comportamiento simplemente deshabilitando el control (Enabled=false on XAML or code).

https://github.com/jesulink2514/XamBooksApp/tree/feature/touch-progress