Wednesday 21 March 2018

Circle Image in Xamarin.Forms using CustomRenderer

Introduction
Circular images in apps are nowadays very popular in-app development. Most of the applications we need to make user profile as circle image e.t.c. So in this article, we can learn how to make Image circle using CustomRenderer.
Requirements
  • This article source code is prepared by using Visual Studio. And it is better to install the latest Visual Studio updates from here.
  • This article is prepared on a MAC machine.
  • This sample project is Xamarin.Forms PCL project.
  • This sample app is targeted for Android, iOS. And tested for Android & iOS.
Description
The creation of a Xamarin.Forms project is very simple in Visual Studio for Mac. It will create three projects:
  1. Shared Code
  2. Xamarin.Android
  3. Xamarin.iOS
The Mac system with Visual Studio for Mac doesn't support Windows projects (UWP, Windows, Windows Phone).
The following steps will show you how to create Xamarin.Forms projects in a Mac system with Visual Studio.
First, open Visual Studio for Mac. And click on New Project.

After that, we need to select whether you're doing Xamarin.Forms or Xamarin.Android or Xamarin.iOS project. If we want to create Xamarin.Forms project just follow the below screenshot.

Then, we have to give the app name; i.e., ImageCircleDemo.

Note:
In the above screen under Shared Code, select Portable class Library or Use Shared Library.
Then, click on Next Button and the following screenshot will be displayed. In that screen, we have to browse the file path where we want to save that application on our PC.

After clicking on the create button it will create the ImageCircleDemo Xamarin.Forms project like below.

And the project structure will be:
ImageCircleDemo:  It is for Shared Code
ImageCircleDemo.Droid:  It is for Android.
ImageCircleDemo.iOS:  It is for iOS

We need to follow the below steps to make for ImageCircle.

Portable Class Library (PCL)


Step 1:
In PCL, create a class name ImageCircle which should inherit any Image like below.

ImageCircleDemo.cs
  1. using Xamarin.Forms;    
  2.     
  3. namespace ImageCircleDemo.CustomControls    
  4. {    
  5.     public class ImageCircle : Image    
  6.     {    
  7.     
  8.     }    
  9. }    
Step 2:
Create your own Xaml page named ImageCirclePage.xaml, and make sure to refer to "ImageCircle" class in Xaml by declaring a namespace for its location and using the namespace prefix on the control element. The following code example shows how the "ImageCircle" renderer class can be consumed by a Xaml page:

ImageCirclePage.xaml
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" BackgroundColor="White" xmlns:custom="clr-namespace:ImageCircleDemo.CustomControls;assembly=ImageCircleDemo" 
  3. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  4. x:Class="ImageCircleDemo.Views.ImageCirclePage">    
  5.     <Grid Padding="10,20,10,10" RowSpacing="10">    
  6.         <Grid.RowDefinitions>    
  7.             <RowDefinition Height="Auto"/>    
  8.             <RowDefinition Height="*"/>    
  9.             <RowDefinition Height="Auto"/>    
  10.             <RowDefinition Height="Auto"/>    
  11.         </Grid.RowDefinitions>    
  12.         <Label Text="Normal Image" Grid.Row="0" FontSize="20"  HorizontalOptions="CenterAndExpand"/>    
  13.         <Image Aspect="AspectFill" Source="BG.png" Grid.Row="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"/>    
  14.          <Label Text="Circle Image" Grid.Row="2" FontSize="20"  HorizontalOptions="CenterAndExpand"/>    
  15.         <custom:ImageCircle Source="BG.png" Grid.Row="3" WidthRequest="100" HeightRequest="100" Aspect="AspectFill" VerticalOptions="Start"  HorizontalOptions="CenterAndExpand"/>    
  16.     </Grid>    
  17. </ContentPage>    
Note:
The "custom" namespace prefix can be named anything. However, the clr-namespace and assembly values must match the details of the custom renderer class. Once the namespace is declared the prefix is used to reference the custom control.

ImageCirclePage.xaml.cs
  1. using Xamarin.Forms;    
  2.     
  3. namespace ImageCircleDemo.Views    
  4. {    
  5.     public partial class ImageCirclePage : ContentPage    
  6.     {    
  7.         public ImageCirclePage()    
  8.         {    
  9.             InitializeComponent();    
  10.         }    
  11.     }    
  12. }    
Xamarin.Andriod
In android project, create a class with the name ImageCircleRenderer.

ImageCircleRenderer.cs
  1. using System;    
  2. using Android.Graphics;    
  3. using Android.Views;    
  4. using ImageCircleDemo.CustomControls;    
  5. using ImageCircleDemo.Droid.CustomControls;    
  6. using Xamarin.Forms;    
  7. using Xamarin.Forms.Platform.Android;    
  8.     
  9. [assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]    
  10. namespace ImageCircleDemo.Droid.CustomControls    
  11. {    
  12.     public class ImageCircleRenderer : ImageRenderer    
  13.     {    
  14.     
  15.         protected override void OnElementChanged(ElementChangedEventArgs<Image> e)    
  16.         {    
  17.             base.OnElementChanged(e);    
  18.     
  19.             if (e.OldElement == null)    
  20.             {    
  21.                 if ((int)Android.OS.Build.VERSION.SdkInt < 18)    
  22.                     SetLayerType(LayerType.Software, null);    
  23.             }    
  24.         }    
  25.     
  26.         protected override bool DrawChild(Canvas canvas, global::Android.Views.View child, long drawingTime)    
  27.         {    
  28.             try    
  29.             {    
  30.                 var radius = Math.Min(Width, Height) / 2;    
  31.                 var strokeWidth = 10;    
  32.                 radius -= strokeWidth / 2;    
  33.     
  34.                 Path path = new Path();    
  35.                 path.AddCircle(Width / 2, Height / 2, radius, Path.Direction.Ccw);    
  36.                 canvas.Save();    
  37.                 canvas.ClipPath(path);    
  38.     
  39.                 var result = base.DrawChild(canvas, child, drawingTime);    
  40.     
  41.                 canvas.Restore();    
  42.     
  43.                 path = new Path();    
  44.                 path.AddCircle(Width / 2, Height / 2, radius, Path.Direction.Ccw);    
  45.     
  46.                 var paint = new Paint();    
  47.                 paint.AntiAlias = true;    
  48.                 paint.StrokeWidth = 5;    
  49.                 paint.SetStyle(Paint.Style.Stroke);    
  50.                 paint.Color = global::Android.Graphics.Color.White;    
  51.     
  52.                 canvas.DrawPath(path, paint);    
  53.     
  54.                 paint.Dispose();    
  55.                 path.Dispose();    
  56.                 return result;    
  57.             }    
  58.             catch (Exception ex)    
  59.             {    
  60.                 var msg = ex.Message;    
  61.             }    
  62.     
  63.             return base.DrawChild(canvas, child, drawingTime);    
  64.         }    
  65.     }    
  66. }    
The call to the base class's OnElementChanged method instantiates an Android ImageView, with a reference to the control being assigned to the renderer's property. The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

Output:

Xamarin.iOS
In iOS project,  create a class with the name ImageCircleRenderer.

ImageCircleRenderer.cs
  1. using System;    
  2. using System.ComponentModel;    
  3. using System.Diagnostics;    
  4. using ImageCircleDemo.CustomControls;    
  5. using ImageCircleDemo.iOS.CustomControls;    
  6. using Xamarin.Forms;    
  7. using Xamarin.Forms.Platform.iOS;    
  8.     
  9. [assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]    
  10. namespace ImageCircleDemo.iOS.CustomControls    
  11. {    
  12.     public class ImageCircleRenderer : ImageRenderer    
  13.     {    
  14.         protected override void OnElementChanged(ElementChangedEventArgs<Image> e)    
  15.         {    
  16.             base.OnElementChanged(e);    
  17.     
  18.             if (e.OldElement != null || Element == null)    
  19.                 return;    
  20.     
  21.             CreateCircle();    
  22.         }    
  23.     
  24.         protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)    
  25.         {    
  26.             base.OnElementPropertyChanged(sender, e);    
  27.     
  28.             if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||    
  29.                 e.PropertyName == VisualElement.WidthProperty.PropertyName)    
  30.             {    
  31.                 CreateCircle();    
  32.             }    
  33.         }    
  34.         private void CreateCircle()    
  35.         {    
  36.             try    
  37.             {    
  38.                 double min = Math.Min(Element.Width, Element.Height);    
  39.                 Control.Layer.CornerRadius = (float)(min / 2.0);    
  40.                 Control.Layer.MasksToBounds = false;    
  41.                 Control.Layer.BorderColor = Color.White.ToCGColor();    
  42.                 Control.Layer.BorderWidth = 1;    
  43.                 Control.ClipsToBounds = true;    
  44.             }    
  45.             catch (Exception ex)    
  46.             {    
  47.                 Debug.WriteLine("Unable to create circle image: " + ex);    
  48.             }    
  49.         }    
  50.     }    
  51. }    
The call to the base class's method OnElementChanged instantiates an iOS control UIImage, with a reference to the control being assigned to the renderer's property Control.

Output:
                     
Please download the sample from below.


No comments:

Post a Comment