Abgerundete Images (CircleImage) in Xamarin

Um abgerundete Bilder (CircleImage) in Anwendungen zu nutzen, gibt es natürlich die Möglichkeit das Bild mittels Bildbearbeitung zu verrunden und anschließend als transparenzfähige Datei abzuspeichern. Aber was ist, wenn man das Bild z.B. von Gravatar nachladen möchte und dieses rund darstellen möchte? Nun, für Xamarin gibt es hier eine relativ einfache Lösung, welche dank plattformspezifischen Renderer auch über verschiedene Plattformen hinweg genutzt werden kann. Zunächst erstellen wir uns hierzu eine leere Klasse, welche von Xamarin.Forms.Image ableitet (alternativ würden wir alle Images in der Anwendung rund darstellen). Da dies eine leere Klasse ist, welche nur von der Basisklasse ableitet, erwähne ich diese hier nicht weiter. In meinem Beispiel heißt die Klasse „CirlceImage“. Weiter geht es zu den plattformspezifischen Renderern. 

iOS

Unter iOS benötigen wir usings auf Folgendes: System, System.ComponentModel, System.Diagnostics (falls das Debug.WriteLine gewünscht ist), Xamarin.Forms und Xamarin.Forms.Platform.iOS. Und nun zum Code für die runden Images:

[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
public class CircleImageRenderer : ImageRenderer
{
	protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
	{
		base.OnElementChanged(e);
		if (e.OldElement != null || Element == null)
			return;

		CreateCircle(e.NewElement);
	}

	protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
	{
		base.OnElementPropertyChanged(sender, e);

		if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||
			e.PropertyName == VisualElement.WidthProperty.PropertyName)
		{
			CreateCircle(Element);
		}
	}

	private void CreateCircle(Image element)
	{
		try
		{
			double min = Math.Min(element.Width, element.Height);
			Control.Layer.CornerRadius = (float)(min / 2.0);
			Control.Layer.MasksToBounds = false;
			Control.Layer.BorderColor = Color.Black.ToCGColor();
			Control.Layer.BorderWidth = 1;
			Control.ClipsToBounds = true;
		}
		catch (Exception ex)
		{
			Debug.WriteLine("Unable to create circle image: " + ex);
		}
	}
}

Android

Unter Android sieht es mit den usings schon ein wenig anders aus. Hier werden folgende benötigt: Android.Graphics, Java.Lang, Xamarin.Forms sowie Xamarin.Forms.Platform.Android. Der Code hier sieht wie folgt aus:

[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
public class CircleImageRenderer : ImageRenderer
{
	protected override bool DrawChild(Canvas canvas, Android.Views.View child, long drawingTime)
	{
		try
		{
			var radius = Math.Min(Width, Height) / 2;
			var strokeWidth = 10;
			radius -= strokeWidth / 2;

			var path = new Path();
			path.AddCircle(Width / 2f, Height / 2f, radius, Path.Direction.Ccw);
			canvas.Save();
			canvas.ClipPath(path);

			var result = base.DrawChild(canvas, child, drawingTime);
			canvas.Restore();

			path = new Path();
			path.AddCircle(Width / 2f, Height / 2f, radius, Path.Direction.Ccw);

			var paint = new Paint
				{
					AntiAlias = true,
					StrokeWidth = 1
				};

			paint.SetStyle(Paint.Style.Stroke);
			paint.Color = Android.Graphics.Color.Black;

			canvas.DrawPath(path, paint);

			paint.Dispose();
			path.Dispose();
			return result;
		}
		catch (Exception)
		{ }

		return base.DrawChild(canvas, child, drawingTime);
	}
}

Fazit

Auch wenn es mehr oder weniger nur eine optische Spielerei ist, so finde ich es persönlich sehr nett einfach abgerundete Bilder verwenden zu können, ohne diese zuvor zu bearbeiten. Wer den schwarzen Rand nicht möchte, kann unter iOS die BorderWidth und unter Android die StrokeWidth einfach auf 0 setzen oder die Farbe verändern.