Xamarin.Tip – Create an Initials Circle View

A while back, I put up a helpful Xamarin.Tip on how to create a simple CircleView in Xamarin.Forms for a BadgeView control – Xamarin.Controls – BadgeView

Since then, I’ve used this simple concept to create some more useful controls that are unique and dynamic in their own way. In this post, we will look at using the CircleView to create an InitialsCircleView. This view is a simple Xamarin.Forms control to show a person’s initials. It is extremely useful in place of user avatars or profile pictures and can add a nice touch to your User Interface without much work at all.

Let’s start by re-iterating how to build a simple CircleView:

In your Xamarin.Forms project, create a new class:

CircleView.cs

public 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()
    {
    }
}

And create your native renderers:

AndroidCircleViewRenderer.cs

public class CircleViewRenderer : BoxRenderer
{
    private float _cornerRadius;
    private RectF _bounds;
    private Path _path;

    public CircleViewRenderer(Context context)
        : base(context)
    {

    }

    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 == null)
        {
            _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();
    }
}

iOSCircleViewRenderer.cs

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;
    }
}

Okay, cool we can draw circles in Xamarin.Forms easily with

<suave:CircleView .../>

So now let’s build a Xamarin.Forms component on top of this for our InitialsCircleView. The key pieces to making this view unique and cool is:

  • Circle color
  • Font size
  • Font color
  • Font family
  • Circle Radius (in case you don’t really want a circle)

Let’s write the ContentView in XAML, but you could easily do it in C# too:

InitialsCircleView.xaml

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:components="clr-namespace:SuaveControls.Components"
    x:Class="SuaveControls.Components.InitialsCircleView">
    <ContentView.Content>
        <Grid x:Name="Container">
            <components:CircleView x:Name="Circle" Margin="16" HorizontalOptions="Fill" VerticalOptions="Fill" Color="{Binding CircleColor}" CornerRadius="{Binding CornerRadius}"/>
            <Label x:Name="InitialsLabel" VerticalOptions="Center" HorizontalOptions="Center" HorizontalTextAlignment="Center" TextColor="{Binding TextColor}" Font="{Binding Font}" FontSize="{Binding FontSize}" />
        </Grid>
    </ContentView.Content>
</ContentView>

So now we have the layout of our InitialsCirlceView, let’s look at the code behind to see how we apply all these different properties and bind them to this internal view:

InitialsCircleView.xaml.cs

public partial class InitialsCircleView : ContentView
{
    public static BindableProperty CircleColorProperty = BindableProperty.Create(nameof(CircleColor), typeof(Color), typeof(InitialsCircleView), Color.White);
    public static BindableProperty TextColorProperty = BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(InitialsCircleView), Color.White);
    public static BindableProperty FontSizeProperty = BindableProperty.Create(nameof(FontSize), typeof(int), typeof(InitialsCircleView), 14);
    public static BindableProperty FontProperty = BindableProperty.Create(nameof(Font), typeof(Font), typeof(InitialsCircleView), Font.Default);
    public static BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(InitialsCircleView), 0.0, propertyChanged: (bindable, oldVal, newVal) =>
    {
        var initialsView = bindable as InitialsCircleView;
        if (initialsView != null)
            initialsView.UpdateCornerRadius((double)newVal);
    });
    public static BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(InitialsCircleView), string.Empty, 
    propertyChanged: (bindable, oldVal, newVal) =>
    {
        var initialsView = bindable as InitialsCircleView;
        if (initialsView != null)
            initialsView.UpdateTextWithName(newVal?.ToString());
    });

    public double CornerRadius
    {
        get
        {
            return (double)GetValue(CornerRadiusProperty);
        }
        set
        {
            SetValue(CornerRadiusProperty, value);
        }
    }
    public string Name
    {
        get
        {
            return (string)GetValue(NameProperty);
        }
        set
        {
            SetValue(NameProperty, value);
        }
    }
    public int FontSize
    {
        get
        {
            return (int)GetValue(FontSizeProperty);
        }
        set
        {
            SetValue(FontSizeProperty, value);
        }
    }
    public Font Font
    {
        get
        {
            return (Font)GetValue(FontProperty);
        }
        set
        {
            SetValue(FontProperty, value);
        }
    }
    public Color CircleColor
    {
        get
        {
            return (Color)GetValue(CircleColorProperty);
        }
        set
        {
            SetValue(CircleColorProperty, value);
        }
    }
    public Color TextColor
    {
        get
        {
            return (Color)GetValue(TextColorProperty);
        }
        set
        {
            SetValue(TextColorProperty, value);
        }
    }


    public InitialsCircleView()
    {
        InitializeComponent();
        Container.BindingContext = this;
    }

    /// <summary>
    /// Updates the name of the text with.
    /// </summary>
    /// <param name="name">Name.</param>
    private void UpdateTextWithName(string name)
    {
        if (string.IsNullOrEmpty(name))
            return;

        var separateWords = name.Split(' ');
        if(separateWords.Length > 0)
        {
            var initialsArray = separateWords.Select(word => word[0].ToString().ToUpper()).ToArray(); // array of string of initials upper cased
            if(initialsArray.Length > 1)
            {
                // grab the first and last
                initialsArray = new string[2] { initialsArray[0], initialsArray[initialsArray.Length - 1] };
            }
            var initialsString = string.Join(string.Empty, initialsArray);
            InitialsLabel.Text = initialsString;
        }
        else
        {
            InitialsLabel.Text = string.Empty;
        }
    }

    /// <summary>
    /// Updates the corner radius.
    /// </summary>
    /// <param name="radius">Radius.</param>
    private void UpdateCornerRadius(double radius)
    {
        Circle.CornerRadius = radius;
    }
}

We have a few properties that are directly bound to the subviews in the XAML which is facilitated by applying the Container.BindingContext = this;.

We also have a property for Name which invokes the internal UpdateTextWithName method. This in turn takes the name of a person, grabs the initials, and sets the text of the Label to it. So we can then use it by just passing a person or thing’s name and let it figure out the initials naturally. So if we say:

var initialsCirle = new InitialsCirlceView
{
    Name = "Alex Dunn"
}

The output will be “AD” in the cirlce.

Here’s how you can now use it in your XAML:

MainPage.xaml

...
<components:InitialsCircleView 
    CircleColor="Red"
    FontSize="32" 
    Name="Alex Dunn" 
    VerticalOptions="Center" 
    HorizontalOptions="Center" 
    TextColor="White" 
    CornerRadius="90" 
    WidthRequest="120" 
    HeightRequest="120"/>

...

Here’s what it looks like!
InitialsCircle

You can also create XAML Styles for it instead of managing all the colors and font options everywhere you use it.


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!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

3 thoughts on “Xamarin.Tip – Create an Initials Circle View”

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 )

Google+ photo

You are commenting using your Google+ 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 )

w

Connecting to %s