Xamarin.Controls – BadgeView

Who knew it could be so difficult to just put a number on a circle in Xamarin.Forms? Here’s a freebie to make your life easier in creating your own badges.

Before we dive in, I want to note that with the Xamarin.Forms.Themes, this can be easier. Check out the docs here: https://developer.xamarin.com/guides/xamarin-forms/user-interface/themes/. Basically, they added a StyleClass for BoxView that allows you to render it as a circle (although I’ve had problems with it in the past). This example is going to be avoiding the Themes package with a custom rolled implementation.

To start, we are going to create a custom CircleView. That CircleView is going to inherit from BoxView and use a custom renderer to give us the rounded edges we want. After that, we are going to make a reusable view called BadgeView that will essentially just slap a Label on top of our new CircleView.

Start here, with your CircleView in your PCL or Shared Library:

CircleView.cs

    public partial class CircleView : BoxView
    {
        public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(CircleView), 0.0);

        public double CornerRadius
        {
            get { return (double)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public CircleView()
        {
            InitializeComponent();
        }
    }

Now let’s create our custom renderers. I want to note, that for iOS it is much simpler, and could also be done as an Effect rather than a BoxRenderer, however, in order to be consistent with the more complicated Android implementation, we are doing both as renderers.

First, and easiest – iOS:

CircleViewRenderer.cs


[assembly: ExportRenderer(typeof(CircleView), typeof(CircleViewRenderer))]
namespace YourNamespace.iOS
{
    public class CircleViewRenderer : BoxRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
                return;

            Layer.MasksToBounds = true;
            Layer.CornerRadius = (float)((CircleView)Element).CornerRadius / 2.0f;
        }

    }
}

and of course Android:

CircleViewRenderer.cs


[assembly: ExportRenderer(typeof(CircleView), typeof(CircleViewRenderer))]
namespace YouNamespace.Droid
{
    public class CircleViewRenderer : BoxRenderer
    {
        private float _cornerRadius;
        private RectF _bounds;
        private Path _path;
        protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
            {
                return;
            }
            var element = (CircleView)Element;

            _cornerRadius = TypedValue.ApplyDimension(ComplexUnitType.Dip, (float)element.CornerRadius, Context.Resources.DisplayMetrics);

        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);
            if (w != oldw && h != oldh)
            {
                _bounds = new RectF(0, 0, w, h);
            }

            _path = new Path();
            _path.Reset();
            _path.AddRoundRect(_bounds, _cornerRadius, _cornerRadius, Path.Direction.Cw);
            _path.Close();
        }

        public override void Draw(Canvas canvas)
        {
            canvas.Save();
            canvas.ClipPath(_path);
            base.Draw(canvas);
            canvas.Restore();
        }
    }
}

Cool. Now we can draw pretty circles in our Xamarin.Forms views:

<views:CircleView CornerRadius="16" WidthRequest="16" HeightRequest="16"/>

Now let’s apply that to a reusable BadgeView.

BadgeView.xaml

<Grid      xmlns="http://xamarin.com/schemas/2014/forms"      xmlns:local="clr-namespace:mHealthConnect.Clients.Forms.Views.Components;assembly=mHealthConnect.Clients.Forms"     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"      x:Class="mHealthConnect.Clients.Forms.Views.Components.BadgeView"     HeightRequest="16"     WidthRequest="16">
    <local:CircleView x:Name="BadgeCircle" HeightRequest="16" WidthRequest="16" CornerRadius="16" VerticalOptions="Center" HorizontalOptions="Center" />
    <Label x:Name="BadgeLabel" TextColor="White" VerticalOptions="Center" HorizontalOptions="Center" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" FontSize="10"/>
</Grid>

BadgeView.xaml.cs

 public partial class BadgeView : Grid
    {
        public static BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(BadgeView), "0", propertyChanged: (bindable, oldVal, newVal) =>
        {
            var view = (BadgeView)bindable;
            view.BadgeLabel.Text = (string)newVal;
        });

        public static BindableProperty BadgeColorProperty = BindableProperty.Create("BadgeColor", typeof(Color), typeof(BadgeView), Color.Blue, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var view = (BadgeView)bindable;
            view.BadgeCircle.BackgroundColor = (Color)newVal;
        });

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
        public Color BadgeColor
        {
            get
            {
                return (Color)GetValue(BadgeColorProperty);
            }
            set
            {
                SetValue(BadgeColorProperty, value);
            }
        }
        public BadgeView()
        {
            InitializeComponent();
            BadgeLabel.Text = Text;
            BadgeCircle.BackgroundColor = BadgeColor;
        }
    }

This is obviously a super simple example, but you can always add any other properties you want such as handling changing sizes, corners, shapes, colors, etc.

But now we can see our final results when using our control:

 <Grid>
    <Label HorizontalTextAlignment="Center" Text="Look at me!"/>
    <views:BadgeView Text="3" BadgeColor="Green" VerticalOptions="Start" HorizontalOptions="End"/>
</Grid>

BadgeExample

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Advertisements

18 thoughts on “Xamarin.Controls – BadgeView”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s