GradientProgressBar: Accepting touch input Skiasharp

Hi techies, this is another quick post about SkiaSharp and this little control made with it.

When I published a post about SkiaSharp (you can check it out here) some people told me whether or not I could make this progress bar editable, so a user can click and update the progress. So let's do it and reviewing how SkiaSharp handles touch events.

TL;DR;

If you prefer to check the code directly you could go directly to the repository on Github https://github.com/jesulink2514/XamBooksApp/tree/feature/touch-progress

Let's do it

Ok, to begin with, we must provide a constructor to our control and enable touch events in SkiaSharp, easy.

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

In order to calculate the percentage based on touch position, I need a scale factor to convert between Xamarin.Forms units and SkiaSharp units (kind of pixel unit) so I defined a protected float field called ScaleFactor;

protected float ScaleFactor;

I've been calculating this factor in OnPaintSurface so I just need to set this value to our ScaleFactor field.

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;
    
    ...

With all this plumbing in place (not much to be honest), I can show you the magic. I should override the OnTouch method in order to process touch events. This method receives an instance of SKTouchEventArgs that give us all information available about this interaction.

I interested in handling changes while users are still touching the progress bar, not only at the start of interaction or the end, so I handled SKTouchAction.Pressed, SKTouchAction.Released and SKTouchAction.Moved. You can check more detail about this enum here.

Then, I must calculate the new percentage in our progress bar considering touch position, for this our SKTouchEventArgs instance (e) provides me with Location property and X value.

Thinking about math here, if I know 100% width and current x value, based on the rule of 3, the percentage would be equal to current  x value divided by  100% width value.

I like to add this Math.Max for the percentage to avoid values falling behind zero and this Math.Min for Location.X to avoid values going further than 100%.

And the final touch (=D funny if you caught it), is the e.Handled = true, to avoid event bubbling.

And that's it, our control receives touch input to set percentage if you required.

PD: You can turn off this behaviour just setting this control to disabled (Enabled=false on XAML or code).

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