Xamarin.Forms - Design Catclock Using SkiaSharp

Introduction to SkiaSharp
  1. SkiaSharp is a cross-platform 2D graphics API for .NET platforms.
  2. SkiaSharp Views & Layers is a set of platform-specific Views and Containers that can be used to draw on the screen and it's used in Google Chrome, Mozilla Firefox, and Android devices.
  3. Binding for Windows.Forms, WPF, iOS, tvOS, Mac, Android, Xamarin.Android and Xamarin.Forms and it can be used across mobile, server, and desktop models to render the images.
  4. Skia Graphics Engine was acquired by Google in 2005 and it's an open source package.
More details: github.com/mono/skiasharp

NuGet Package : Xamarin.Android -->search "SkiaSharp"

Xamarin.Forms -->search"SkiaSharp.Views.Forms"

Properties of SkiaSharp
  • SKcanvas.
  • SKGLView.
  • SKPaint.
  • SKPath.
  • SKShader.
  • SKPathEffect .
Now, let us discuss the attributes of SkiaSharp properties.

Drawing on SKcanvas

The SKcanvas is used for drawing models, lines, arcs, text, and images.

Drawing Methods of SKcanvas
  1. DrawCircle.
  2. DrawRect.
  3. DrawLine.
  4. DrawText.
  5. DrawBitmap.
  6. DrawPath.
Transforms for SKcanvas
  1. Translate.
  2. Scale.
  3. RotateDegrees.
  4. RotateRadians.
Drawing attributes for SKPaint
  1. Style: Storke, Fill, or both.
  2. Color: SKColor value.
  3. StrokeWidth.
  4. StrokeCap: Butt, Square, or Round.
  5. Strokejoin: Miter, Bevel, or Round.
  6. Shader: gradients or bitmap tilling.
 SKPath --> Connected lines and curves
  1. MoveTo and LineTo.
  2. ArcTo: Three kinds of arc definitions.
  3. CubicTo and QuadTo: Bezier.
  4. SVG path date in and out.
 SKShader --> Fancy filles
  1. CreateLinearGradient.
  2. CreateRadialGradient.
  3. CreateSweepGradient.
  4. CreateTwoPointConicalGradient.
  5. CreatePerlinNoise.
  6. CreateBitmap: Tile with SKBitmap.
 SKPathEffects -->fancy strokes
  1. CreateDash.
  2. Create1DPathof: Dashed with another path.
  3. Create2DPath: Storked with an array of another path.
  4. CreateDiscrete: Jitter effect.
 The below article contains just a few but many different things can be done by catclock with SkiaSharp.
 
Let's start.
 
Step 1

You can get started with a new Xamarin.Forms app by going to File >>New >>Visual C#>>Cross-platform >>select Cross-platform App(Xamarin.Native or Xamarin.Forms) and give the application a suitable name, such as CatClock. Choose your project location, then click OK.



Step 2

In this step, add the following NuGet Packages for your project.
  • SkiaSharp
  • SkiaSharp.Views
  • SkiaSharp.Views.Forms               
For that, go to Solution Explorer and select your solution, right click and select "Manage NuGet Packages for the solution".
      
Now, select the following NuGet package and install it.
  • SkiaSharp.Views.Forms 

Step 3

In this step, add a SkiaSharp control to your projects. For that, go to Solution Explorer >>catclock(PCL)>> double click on MainPage.xaml.

After opening this, you can add SkiaSharp assembly and XAML code to your project. Write the code given below.

Assembly
  1. xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms" 
XAML Code
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4.              xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"  
  5.              xmlns:local="clr-namespace:CatClock"  
  6.              x:Class="CatClock.MainPage">  
  7.     <ContentPage.Padding>  
  8.         <OnPlatform x:TypeArguments="Thickness"  
  9.                     iOS="0, 20, 0, 0" />  
  10.     </ContentPage.Padding>  
  11.   
  12.     <skia:SKCanvasView x:Name="canvasView"  
  13.                        PaintSurface="canvasView_PaintSurface" />  
  14. </ContentPage>   
 
 
Step 4

Now, we need to add a picture for background display. For that, go to Solution Explorer >> Add >> Existing item >> select one picture and click OK.

 
  
After this, let's change the picture properties to embedded resource. For that, go to Properties window >> Advanced >> Build Action>>select Embedded Resource.

 
 
Step 5

In this step, open Solution Explorer>>catclock(PCL)>>Mainpage.xaml.cs page and double-click to open the design view. Here is the code for this page.
 
CS code
  1. using System;  
  2. using System.IO;  
  3. using System.Reflection;  
  4. using Xamarin.Forms;  
  5. using SkiaSharp;  
  6. using SkiaSharp.Views.Forms;  
  7.   
  8. namespace CatClock  
  9. {  
  10.     public partial class MainPage : ContentPage  
  11.     {  
  12.         SKPaint blackFillPaint = new SKPaint  
  13.         {  
  14.             Style = SKPaintStyle.Fill,  
  15.             Color = SKColors.Black  
  16.         };  
  17.   
  18.         SKPaint whiteStrokePaint = new SKPaint  
  19.         {  
  20.             Style = SKPaintStyle.Stroke,  
  21.             Color = SKColors.White,  
  22.             StrokeWidth = 2,  
  23.             StrokeCap = SKStrokeCap.Round,  
  24.             IsAntialias = true  
  25.         };  
  26.   
  27.         SKPaint whiteFillPaint = new SKPaint  
  28.         {  
  29.             Style = SKPaintStyle.Fill,  
  30.             Color = SKColors.White  
  31.         };  
  32.   
  33.         SKPaint greenFillPaint = new SKPaint  
  34.         {  
  35.             Style = SKPaintStyle.Fill,  
  36.             Color = SKColors.PaleGreen  
  37.         };  
  38.   
  39.         SKPaint blackStrokePaint = new SKPaint  
  40.         {  
  41.             Style = SKPaintStyle.Stroke,  
  42.             Color = SKColors.Black,  
  43.             StrokeWidth = 20,  
  44.             StrokeCap = SKStrokeCap.Round  
  45.         };  
  46.   
  47.         SKPaint grayFillPaint = new SKPaint  
  48.         {  
  49.             Style = SKPaintStyle.Fill,  
  50.             Color = SKColors.Gray  
  51.         };  
  52.   
  53.         SKPaint backgroundFillPaint = new SKPaint  
  54.         {  
  55.             Style = SKPaintStyle.Fill  
  56.         };  
  57.   
  58.         SKPath catEarPath = new SKPath();  
  59.         SKPath catEyePath = new SKPath();  
  60.         SKPath catPupilPath = new SKPath();  
  61.         SKPath catTailPath = new SKPath();  
  62.   
  63.         SKPath hourHandPath = SKPath.ParseSvgPathData(  
  64.             "M 0 -60 C 0 -30 20 -30 5 -20 L 5 0 C 5 7.5 -5 7.5 -5 0 L -5 -20 C -20 -30 0 -30 0 -60");  
  65.         SKPath minuteHandPath = SKPath.ParseSvgPathData(  
  66.             "M 0 -80 C 0 -75 0 -70 2.5 -60 L 2.5 0 C 2.5 5 -2.5 5 -2.5 0 L -2.5 -60 C 0 -70 0 -75 0 -80");  
  67.   
  68.         public MainPage()  
  69.         {  
  70.             InitializeComponent();  
  71.   
  72.             // Make cat ear path  
  73.             catEarPath.MoveTo(0, 0);  
  74.             catEarPath.LineTo(0, 75);  
  75.             catEarPath.LineTo(100, 75);  
  76.             catEarPath.Close();  
  77.   
  78.             // Make cat eye path  
  79.             catEyePath.MoveTo(0, 0);  
  80.             catEyePath.ArcTo(50, 50, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 50, 0);  
  81.             catEyePath.ArcTo(50, 50, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 0, 0);  
  82.             catEyePath.Close();  
  83.   
  84.             // Make eye pupil path  
  85.             catPupilPath.MoveTo(25, -5);  
  86.             catPupilPath.ArcTo(6, 6, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 25, 5);  
  87.             catPupilPath.ArcTo(6, 6, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 25, -5);  
  88.             catPupilPath.Close();  
  89.               
  90.             // Make cat tail path  
  91.             catTailPath.MoveTo(0, 100);  
  92.             catTailPath.CubicTo(50, 200, 0, 250, -50, 200);  
  93.   
  94.             // Create Shader  
  95.             Assembly assembly = GetType().GetTypeInfo().Assembly;  
  96.             using (Stream stream = assembly.GetManifestResourceStream("CatClock.WoodGrain.png"))  
  97.             using (SKManagedStream skStream = new SKManagedStream(stream))  
  98.             using (SKBitmap bitmap = SKBitmap.Decode(skStream))  
  99.             using (SKShader shader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror))  
  100.             {  
  101.                 backgroundFillPaint.Shader = shader;  
  102.             }  
  103.   
  104.             Device.StartTimer(TimeSpan.FromSeconds(1f / 60), () =>  
  105.                 {  
  106.                     canvasView.InvalidateSurface();  
  107.                     return true;  
  108.                 });  
  109.         }  
  110.   
  111.         private void canvasView_PaintSurface(object sender, SKPaintSurfaceEventArgs e)  
  112.         {  
  113.             SKSurface surface = e.Surface;  
  114.             SKCanvas canvas = surface.Canvas;  
  115.   
  116.             canvas.DrawPaint(backgroundFillPaint);  
  117.   
  118.             int width = e.Info.Width;  
  119.             int height = e.Info.Height;  
  120.   
  121.             // Set transforms  
  122.             canvas.Translate(width / 2, height / 2);  
  123.             canvas.Scale(Math.Min(width / 210f, height / 520f));  
  124.   
  125.             // Get DateTime  
  126.             DateTime dateTime = DateTime.Now;  
  127.   
  128.             // Head  
  129.             canvas.DrawCircle(0, -160, 75, blackFillPaint);  
  130.   
  131.             // Draw ears and eyes  
  132.             for (int i = 0; i < 2; i++)  
  133.             {  
  134.                 canvas.Save();  
  135.                 canvas.Scale(2 * i - 1, 1);  
  136.   
  137.                 canvas.Save();  
  138.                 canvas.Translate(-65, -255);  
  139.                 canvas.DrawPath(catEarPath, blackFillPaint);  
  140.                 canvas.Restore();  
  141.   
  142.                 canvas.Save();  
  143.                 canvas.Translate(10, -170);  
  144.                 canvas.DrawPath(catEyePath, greenFillPaint);  
  145.                 canvas.DrawPath(catPupilPath, blackFillPaint);  
  146.                 canvas.Restore();  
  147.   
  148.                 // Draw whiskers  
  149.                 canvas.DrawLine(10, -120, 100, -100, whiteStrokePaint);  
  150.                 canvas.DrawLine(10, -125, 100, -120, whiteStrokePaint);  
  151.                 canvas.DrawLine(10, -130, 100, -140, whiteStrokePaint);  
  152.                 canvas.DrawLine(10, -135, 100, -160, whiteStrokePaint);  
  153.   
  154.                 canvas.Restore();  
  155.             }  
  156.   
  157.             // Move Tail  
  158.             float t = (float)Math.Sin((dateTime.Second % 2 + dateTime.Millisecond / 1000.0) * Math.PI);  
  159.             catTailPath.Reset();  
  160.             catTailPath.MoveTo(0, 100);  
  161.             SKPoint point1 = new SKPoint(-50 * t, 200);  
  162.             SKPoint point2 = new SKPoint(0, 250 - Math.Abs(50 * t));  
  163.             SKPoint point3 = new SKPoint(50 * t, 250 - Math.Abs(75 * t));  
  164.             catTailPath.CubicTo(point1, point2, point3);  
  165.   
  166.             canvas.DrawPath(catTailPath, blackStrokePaint);  
  167.   
  168.             // Clock background  
  169.             canvas.DrawCircle(0, 0, 100, blackFillPaint);  
  170.   
  171.             // Hour and minute marks  
  172.             for (int angle = 0; angle < 360; angle += 6)  
  173.             {  
  174.                 canvas.DrawCircle(0, -90, angle % 30 == 0 ? 4 : 2, whiteFillPaint);  
  175.                 canvas.RotateDegrees(6);  
  176.             }  
  177.   
  178.             // Hour hand  
  179.             canvas.Save();  
  180.             canvas.RotateDegrees(30 * dateTime.Hour + dateTime.Minute / 2f);  
  181.             canvas.DrawPath(hourHandPath, grayFillPaint);  
  182.             canvas.DrawPath(hourHandPath, whiteStrokePaint);  
  183.             canvas.Restore();  
  184.   
  185.             // Minute hand  
  186.             canvas.Save();  
  187.             canvas.RotateDegrees(6 * dateTime.Minute + dateTime.Second / 10f);  
  188.             canvas.DrawPath(minuteHandPath, grayFillPaint);  
  189.             canvas.DrawPath(minuteHandPath, whiteStrokePaint);  
  190.             canvas.Restore();  
  191.   
  192.             // Second hand  
  193.             canvas.Save();  
  194.             float seconds = dateTime.Second + dateTime.Millisecond / 1000f;  
  195.             canvas.RotateDegrees(6 * seconds);  
  196.             whiteStrokePaint.StrokeWidth = 2;  
  197.             canvas.DrawLine(0, 10, 0, -80, whiteStrokePaint);  
  198.             canvas.Restore();  
  199.         }  
  200.     }  
  201. }  
 

Step 6

Now, go to "Build" menu and click "Configure Manager". Here, you can configure your startup projects. Click F5 or Start to build and run your application.
 
 After a few seconds, the app will start running on your Android simulators and you will see your app working successfully.

 
Finally, we have successfully created a Xamarin.Forms catclock with SkiaSharp application.
 
Conclusion

I hope you have learned how to design cat clock application using Visual Studio.