Xamarin Forms Pulse View

I hope you are all well and enjoy my article.
 
Today I am going to show you how to create Pulse View in Xamarin.Forms with Custom Control + SkiaSharp.
 
 
Let's get started,
 
First, we are going to create a demo project and give it whatever name you want. Then we will create a new folder and give it the name Controls for creating the Pulse Control.
 

Implementation

 
First, we will create a Xaman forms project in a Windows machine.
 
 
Open VS2019 >> select file >> Create Project >> and then one dialog window appears.
 
 
This is the new project dialogue window and here,
  1. We can select the cross-platform 
  2. Select Mobile App(Xamarin forms) 
  3. After that, I give the project name like Pulse 
  4. Then we choose the folder path 
  5. Click OK.
 
This is another window in which,
  1. I choose the blank app 
  2. Select the project (Android, iOS) in which I want to create 
  3. Select the Code Sharing Strategy .NET Standard 
  4. Click OK and create the project.
Here we check whether the Xamarin forms Nuget Package is updated or not. If not we will update it.  Following are the steps for updating the package.
 
 
First, we select the solution and right-click on the solution and then select the Nuget package as shown in the below screen.
 
 
Now a new window (Nuget - Solution) appears. This window contains information about the packages used in this project. Here we can select the update tab. All the packages are listed here.
 
Here we can update Xamarin forms package.
 
With this control, we need to add the following package in xamarin.forms.
  • SkiaSharp.Views(Version 1.68.1.1)
  • SkiaSharp.Views.Forms(Version 1.68.1.1)
 
Now we are creating a new class and I gave the name Pulse.cs and below I have written the code of pulse effect.
 
In this class, we add the following properties using  SKCanvasView class.
  1. AutoStart= when the view appears it is autostarted the effect
  2. PulseColor= with PulseColor we set the Color effect
  3. Source= we can set the image
  4. Speed= we can manage the effect speed
Pulse.cs
  1. using SkiaSharp;  
  2. using SkiaSharp.Views.Forms;  
  3. using System;  
  4. using System.Collections.Generic;  
  5. using System.Diagnostics;  
  6. using System.IO;  
  7. using System.Reflection;  
  8. using System.Runtime.CompilerServices;  
  9. using System.Text;  
  10. using Xamarin.Forms;  
  11. namespace PrismSample.Controls {  
  12.     public class Pulse: SKCanvasView {  
  13.         public static readonly BindableProperty PulseColorProperty = BindableProperty.Create(nameof(PulseColor), typeof(Color), typeof(Pulse), Color.Red, propertyChanged: OnPropertyChanged);  
  14.         public static readonly BindableProperty AutoStartProperty = BindableProperty.Create(nameof(AutoStart), typeof(bool), typeof(Pulse), false, propertyChanged: OnPropertyChanged);  
  15.         public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(string), typeof(Pulse), "", propertyChanged: OnPropertyChanged);  
  16.         public static readonly BindableProperty SpeedProperty = BindableProperty.Create(nameof(Speed), typeof(int), typeof(Pulse), 10, propertyChanged: OnPropertyChanged);  
  17.         public Color PulseColor {  
  18.             get {  
  19.                 return (Color) GetValue(PulseColorProperty);  
  20.             }  
  21.             set {  
  22.                 SetValue(PulseColorProperty, value);  
  23.             }  
  24.         }  
  25.         public bool AutoStart {  
  26.             get {  
  27.                 return (bool) GetValue(AutoStartProperty);  
  28.             }  
  29.             set {  
  30.                 SetValue(AutoStartProperty, value);  
  31.             }  
  32.         }  
  33.         public string Source {  
  34.             get {  
  35.                 return (string) GetValue(SourceProperty);  
  36.             }  
  37.             set {  
  38.                 SetValue(SourceProperty, value);  
  39.             }  
  40.         }  
  41.         public int Speed {  
  42.             get {  
  43.                 return (int) GetValue(SpeedProperty);  
  44.             }  
  45.             set {  
  46.                 SetValue(SpeedProperty, value);  
  47.                 cycleTime *= value;  
  48.             }  
  49.         }  
  50.         SKBitmap resourceBitmap;  
  51.         double cycleTime = 30000; // in milliseconds    
  52.         Stopwatch stopwatch = new Stopwatch();  
  53.         public bool IsRun;  
  54.         float[] t = new float[3];  
  55.         SKPaint paint = new SKPaint {  
  56.             Style = SKPaintStyle.Stroke  
  57.         };  
  58.         private static void OnPropertyChanged(BindableObject bindable, object oldVal, object newVal) {  
  59.             var pulse = bindable as Pulse;  
  60.             pulse?.InvalidateSurface();  
  61.         }  
  62.         public Pulse() {  
  63.             cycleTime /= Speed;  
  64.         }  
  65.         protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) {  
  66.             base.OnPropertyChanged(propertyName);  
  67.             if (propertyName == nameof(Speed)) {  
  68.                 cycleTime /= Speed;  
  69.             }  
  70.             if (propertyName == nameof(AutoStart)) {  
  71.                 if (AutoStart) start();  
  72.             }  
  73.             if (propertyName == nameof(Source)) {  
  74.                 string resourceID = Source;  
  75.                 Assembly assembly = GetType().GetTypeInfo().Assembly;  
  76.                 using(Stream stream = assembly.GetManifestResourceStream(resourceID)) {  
  77.                     if (stream != null) resourceBitmap = SKBitmap.Decode(stream);  
  78.                 }  
  79.                 resourceBitmap = resourceBitmap?.Resize(new SKImageInfo(90, 90), SKFilterQuality.Medium);  
  80.             }  
  81.         }  
  82.         public void start() {  
  83.             IsRun = true;  
  84.             stopwatch.Start();  
  85.             Xamarin.Forms.Device.StartTimer(TimeSpan.FromMilliseconds(33), () => {  
  86.                 t[0] = (float)(stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime);  
  87.                 if (stopwatch.Elapsed.TotalMilliseconds > cycleTime / 3) t[1] = (float)((stopwatch.Elapsed.TotalMilliseconds - cycleTime / 3) % cycleTime / cycleTime);  
  88.                 if (stopwatch.Elapsed.TotalMilliseconds > cycleTime * 2 / 3) t[2] = (float)((stopwatch.Elapsed.TotalMilliseconds - cycleTime * 2 / 3) % cycleTime / cycleTime);  
  89.                 this.InvalidateSurface();  
  90.                 if (!IsRun) {  
  91.                     stopwatch.Stop();  
  92.                     stopwatch.Reset();  
  93.                 }  
  94.                 return IsRun;  
  95.             });  
  96.         }  
  97.         protected override void OnPaintSurface(SKPaintSurfaceEventArgs args) {  
  98.             base.OnPaintSurface(args);  
  99.             SKImageInfo info = args.Info;  
  100.             SKSurface surface = args.Surface;  
  101.             SKCanvas canvas = surface.Canvas;  
  102.             byte R = (byte)(PulseColor.R * 255);  
  103.             byte G = (byte)(PulseColor.G * 255);  
  104.             byte B = (byte)(PulseColor.B * 255);  
  105.             canvas.Clear();  
  106.             if (IsRun) {  
  107.                 SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);  
  108.                 float baseRadius = Math.Min(info.Width, info.Height) / 12;  
  109.                 float radius = 0;  
  110.                 Console.WriteLine(radius);  
  111.                 for (int i = 0; i < t.Length; i++) {  
  112.                     radius = info.Width / 2 * (t[i]);  
  113.                     paint.Color = new SKColor(R, G, B, (byte)(255 * (1 - t[i])));  
  114.                     paint.Style = SKPaintStyle.Fill;  
  115.                     canvas.DrawCircle(center.X, center.Y, radius, paint);  
  116.                 }  
  117.                 paint.Color = new SKColor(R, G, B);  
  118.                 canvas.DrawCircle(center.X, center.Y, 100, paint);  
  119.                 if (resourceBitmap != null) canvas.DrawBitmap(resourceBitmap, center.X - resourceBitmap.Width / 2, center.Y - resourceBitmap.Height / 2);  
  120.             }  
  121.         }  
  122.     }  
  123. }  
Now we can add pulse control in MainPage.xmal.
 
MainPage.xmal
  1. <Grid>  
  2.    <SkiaPulse:Pulse  
  3.       Margin="0"  
  4.       AutoStart="true"  
  5.       PulseColor="SkyBlue"  
  6.       Source="PrismSample.Images.bluetooth.png"  
  7.       Speed="10" />  
  8. </Grid>  
That’s it, now our Pulse effect is ready is ready to use. Happy Coding