{"id":16007,"date":"2014-12-19T17:17:24","date_gmt":"2014-12-19T22:17:24","guid":{"rendered":"http:\/\/blog.xamarin.com\/?p=16007"},"modified":"2019-04-02T15:54:40","modified_gmt":"2019-04-02T22:54:40","slug":"elegant-circle-images-in-xamarin-forms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/elegant-circle-images-in-xamarin-forms\/","title":{"rendered":"Elegant Circle Images in Xamarin.Forms"},"content":{"rendered":"<p>\t\t\t\tCircular images in apps are currently a popular trend in mobile, and it is a simple technique that can add a lot of polish to your mobile apps. We made use of them in\u00a0this year&#8217;s <a href=\"\/evolve-conference-app\/\" title=\"Evolve Conference Apps\" target=\"_blank\">Xamarin Evolve mobile apps<\/a>, and with a little bit of code and a single custom renderer, you too can transform standard images in Xamarin.Forms into these elegant round images.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-large wp-image-16008\" src=\"\/wp-content\/uploads\/sites\/44\/2019\/04\/MonkeyCircleImages-1024x308.png\" alt=\"MonkeyCircleImages\" width=\"600\" \/><\/p>\n<h2>Custom Image<\/h2>\n<p>After you create your Xamarin.Forms project, the first thing you need to do is create a custom control that inherits from Image. Let&#8217;s call this <code>ImageCircle<\/code> and then implement the custom renderers on each platform.<\/p>\n<pre class=\"lang:csharp decode:true\">\r\npublic class ImageCircle : Image\r\n{\r\n  \/\/Optional custom properties could go here\r\n}\r\n<\/pre>\n<h2>Custom Renderers<\/h2>\n<p>Now you just need to tell Xamarin.Forms how to render this new ImageCircle on each platform by using <a href=\"http:\/\/developer.xamarin.com\/guides\/cross-platform\/xamarin-forms\/custom-renderer\/\" title=\"How to use custom renderers documentation.\" target=\"_blank\">custom renderers<\/a>.<\/p>\n<p>For all three projects, you will need to create an ImageCircleRenderer that inherits from ImageRenderer and then implement how to cut out the circle image.<\/p>\n<pre class=\"lang:csharp decode:true\">\r\npublic class ImageCircleRenderer : ImageRenderer\r\n{\r\n  \/\/...Implementation\r\n}\r\n<\/pre>\n<h3>iOS Renderer<\/h3>\n<p>For iOS you are able to directly interact with the CALayer of our UIImageView to add a corner radius and set the border color.<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nprivate void CreateCircle()\r\n{\r\n  try\r\n  {\r\n    double min = Math.Min(Element.Width, Element.Height);\r\n    Control.Layer.CornerRadius = (float)(min \/ 2.0);\r\n    Control.Layer.MasksToBounds = false;\r\n    Control.Layer.BorderColor = Color.White.ToCGColor();\r\n    Control.Layer.BorderWidth = 3;\r\n    Control.ClipsToBounds = true;\r\n  }\r\n  catch(Exception ex)\r\n  {\r\n    Debug.WriteLine(&quot;Unable to create circle image: &quot; + ex)\r\n  }\r\n}\r\n<\/pre>\n<p>This method should be called on the initial OnElementChanged or when the Height or Width properties are updated:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nprotected override void OnElementChanged(ElementChangedEventArgs e)\r\n{\r\n  base.OnElementChanged(e);\r\n\r\n  if (e.OldElement != null || Element == null)\r\n    return;\r\n\r\n  CreateCircle();\r\n}\r\n\r\nprotected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)\r\n{\r\n  base.OnElementPropertyChanged(sender, e);\r\n\r\n  if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||\r\n      e.PropertyName == VisualElement.WidthProperty.PropertyName)\r\n  {\r\n    CreateCircle();\r\n  }\r\n}\r\n<\/pre>\n<p>And of course you must export the renderer with an assembly export:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\n[assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]\r\nnamespace CircleImage.iOS\r\n{\r\n \/\/...\r\n}\r\n<\/pre>\n<h3>Android Renderer<\/h3>\n<p>Android is a unique platform; you don&#8217;t have a physical layer to interact with, however, you are able to modify how Xamarin.Forms draws the child and cut out a path from the canvas. This method is a bit of complex math, but calculates the path to clip and then adds a circular border around the child.<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nprotected override bool DrawChild(Canvas canvas, global::Android.Views.View child, long drawingTime)\r\n{\r\n  try\r\n  {\r\n    var radius = Math.Min(Width, Height) \/ 2;\r\n    var strokeWidth = 10;\r\n    radius -= strokeWidth \/ 2;\r\n\r\n    \/\/Create path to clip\r\n    var path = new Path();\r\n    path.AddCircle(Width \/ 2, Height \/ 2, radius, Path.Direction.Ccw);\r\n    canvas.Save();\r\n    canvas.ClipPath(path);\r\n\r\n    var result = base.DrawChild(canvas, child, drawingTime);\r\n\r\n    canvas.Restore();\r\n\r\n    \/\/ Create path for circle border\r\n    path = new Path();\r\n    path.AddCircle(Width \/ 2, Height \/ 2, radius, Path.Direction.Ccw);\r\n\r\n    var paint = new Paint();\r\n    paint.AntiAlias = true;\r\n    paint.StrokeWidth = 5;\r\n    paint.SetStyle(Paint.Style.Stroke);\r\n    paint.Color = global::Android.Graphics.Color.White;\r\n\r\n    canvas.DrawPath(path, paint);\r\n\r\n    \/\/Properly dispose\r\n    paint.Dispose();\r\n    path.Dispose();\r\n    return result;\r\n  }\r\n  catch (Exception ex)\r\n  {\r\n    Debug.WriteLine(&quot;Unable to create circle image: &quot; + ex);\r\n  }\r\n\r\n  return base.DrawChild(canvas, child, drawingTime);\r\n}\r\n<\/pre>\n<p>One intricate part here is that Android handles the clipping of paths differently depending on the version of Android. Hardware acceleration for this method was introduced in API 18, so you must tell Android to use software rendering on older devices. You can set the layer type flag in the OnElementChanged method:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nprotected override void OnElementChanged(ElementChangedEventArgs e)\r\n{\r\n  base.OnElementChanged(e);\r\n\r\n  if (e.OldElement == null)\r\n  {\r\n\r\n    if ((int)Android.OS.Build.VERSION.SdkInt &lt; 18)\r\n      SetLayerType(LayerType.Software, null);\r\n  }\r\n}\r\n<\/pre>\n<p>And of course you must export the renderer with an assembly export:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\n[assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]\r\nnamespace CircleImage.Droid\r\n{\r\n \/\/...\r\n}\r\n<\/pre>\n<h3>Windows Phone Renderer<\/h3>\n<p>Last, but not least, we come to Windows Phone, which has a <code>Clip<\/code> property that you can use to create EllipseGeometry to cut out the circle from the image:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nprotected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)\r\n{\r\n  base.OnElementPropertyChanged(sender, e);\r\n  if (Control != null &amp;&amp; Control.Clip == null)\r\n  {\r\n    var min = Math.Min(Element.Width, Element.Height) \/ 2.0f;\r\n    if (min &lt;= 0)\r\n      return;\r\n    Control.Clip = new EllipseGeometry\r\n    {\r\n      Center = new System.Windows.Point(min, min),\r\n      RadiusX = min,\r\n      RadiusY = min\r\n    };\r\n  }\r\n}\r\n<\/pre>\n<p>And of course you must export the renderer with an assembly export:<\/p>\n<pre class=\"lang:csharp decode:true\">\r\n[assembly: ExportRenderer(typeof(ImageCircle), typeof(ImageCircleRenderer))]\r\nnamespace CircleImage.WinPhone\r\n{\r\n \/\/...\r\n}\r\n<\/pre>\n<h2>Custom Cell<\/h2>\n<p>With all of the custom renders in place, you can simply use your new <code>ImageCircle<\/code> anywhere you would like, including custom ViewCells for your ListViews. The key here is to ensure that you request the same Width &amp; Height for your image and set the Aspect to AspectFill to ensure a perfect square to cut your circle from.<\/p>\n<pre class=\"lang:csharp decode:true\">\r\nint photoSize = Device.OnPlatform(50, 50, 80);\r\nvar photo = new ImageCircle\r\n{\r\n  WidthRequest = photoSize,\r\n  HeightRequest = photoSize,\r\n  Aspect = Aspect.AspectFill,\r\n  HorizontalOptions = LayoutOptions.Center\r\n};\r\nphoto.SetBinding(Image.SourceProperty, s =&gt; s.Image);\r\n<\/pre>\n<p><img decoding=\"async\" class=\"aligncenter size-large wp-image-16010\" src=\"\/wp-content\/uploads\/sites\/44\/2019\/04\/CircleImages-1024x611.png\" alt=\"CircleImages\" width=\"500\" \/><\/p>\n<h2>See the Code in Action<\/h2>\n<p>You can grab the entire <a href=\"https:\/\/github.com\/jamesmontemagno\/Xamarin.Plugins\/tree\/master\/ImageCircle\" title=\"Circle Image code on GitHub\" target=\"_blank\">source code from GitHub<\/a>. Additionally, I sat down with Xamarin&#8217;s own Steven Yi at this year&#8217;s Xamarin Evolve to explain how I implemented and used Circle Images in the Xamarin Evolve mobile apps:\n<iframe width=\"560\" height=\"315\" src=\"\/\/www.youtube.com\/embed\/xbYngHgS5e0\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Circular images in apps are currently a popular trend in mobile, and it is a simple technique that can add a lot of polish to your mobile apps. We made use of them in\u00a0this year&#8217;s Xamarin Evolve mobile apps, and with a little bit of code and a single custom renderer, you too can transform [&hellip;]<\/p>\n","protected":false},"author":544,"featured_media":39167,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[4,16],"class_list":["post-16007","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-xamarin-platform","tag-xamarin-forms"],"acf":[],"blog_post_summary":"<p>Circular images in apps are currently a popular trend in mobile, and it is a simple technique that can add a lot of polish to your mobile apps. We made use of them in\u00a0this year&#8217;s Xamarin Evolve mobile apps, and with a little bit of code and a single custom renderer, you too can transform [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/16007","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/544"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=16007"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/16007\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/39167"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=16007"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=16007"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=16007"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}