Search code examples
xamarinxamarin.formslabelstroke

How to add/create label stroke?


I'm trying to add a stroked label to my Xamarin Forms application, at first I thought it would be simple but after hours of googling I couldn't find anything at all.

All I want is the same simple label control with outline/stroke color and thickness properties, something like this:

<Label Text="Label" TextColor="#0B1751" StrokeColor="#0FBEFE" StrokeThickness="3"/>

enter image description here

Thanks in advance.


Solution

  • Do you want to achieve the result like following screenshot?

    enter image description here

    If so, you can create a custom renderer to achieve it.

    In Android, you can create two Textview, make the smaller textview is above the bigger textview. Here is code. In the IOS, you can achieve it by same way.

    [assembly: ExportRenderer(typeof(Label), typeof(MyLabelRenderer))]
    namespace LabelUpdateDemo.Droid
    {
       public class MyLabelRenderer: LabelRenderer
        {
            Context context;
            public MyLabelRenderer(Context context) : base(context) {
                this.context = context;
            }
    
    
    
    
            protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
            {
                base.OnElementChanged(e);
                if (Control != null)
                {
    
                    StrokeTextView strokeTextView = new StrokeTextView(context,Control.TextSize);
                    strokeTextView.Text = e.NewElement.Text;
                    strokeTextView.SetTextColor(Android.Graphics.Color.Purple);
                  //  Control.TextSize=50;
                    SetNativeControl(strokeTextView);
                }
            }
        }
    }
    

    Here is code about StrokeTextView.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Android.App;
    using Android.Content;
    using Android.Graphics;
    using Android.OS;
    using Android.Runtime;
    using Android.Text;
    using Android.Util;
    using Android.Views;
    using Android.Widget;
    using static Android.Graphics.Paint;
    
    namespace LabelUpdateDemo.Droid
    {
        class StrokeTextView : TextView
        {
            private TextView borderText = null;
            float OriTextSize;
            public StrokeTextView(Context context,float OriTextSize) : base(context)
            {
                borderText = new TextView(context);
                borderText.TextSize = OriTextSize;
                this.TextSize = OriTextSize;
              
                init();
            }
            public StrokeTextView(Context context, IAttributeSet attrs) : base(context, attrs)
            {
                borderText = new TextView(context, attrs);
                init();
            }
            public StrokeTextView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
            {
                borderText = new TextView(context, attrs, defStyle);
                init();
            }
    
    
    
            public void init()
            {
                TextPaint tp1 = borderText.Paint;
                tp1.StrokeWidth = 15;         // sets the stroke width                        
                tp1.SetStyle(Style.Stroke);
                borderText.SetTextColor(Color.Blue);  // set the stroke color
                borderText.Gravity = Gravity;
    
            }
    
         
            public override ViewGroup.LayoutParams LayoutParameters { get => base.LayoutParameters; set => base.LayoutParameters = value; }
    
            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                string tt = borderText.Text;
    
    
                if (tt == null || !tt.Equals(this.Text))
                {
                    borderText.Text = Text;
                    this.PostInvalidate();
                }
    
                base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
                borderText.Measure(widthMeasureSpec, heightMeasureSpec);
            }
    
            protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
            {
                base.OnLayout(changed, left, top, right, bottom);
                borderText.Layout(left, top, right, bottom);
            }
    
            protected override void OnDraw(Canvas canvas)
            {
                borderText.Draw(canvas);
                base.OnDraw(canvas);
            }
        }
    }
    

    Here is code in the xamarin forms.

        <Label 
                x:Name = "BTTextStatus"
                FontAttributes="Bold"
                Text="Label"
                FontSize="Large"/>
    

    If you want to add some custom renderers, you can create a custom label, then add behavior for this costom label

    ===============Update==================

    If you want to add properties, such as StrokeColor="#0FBEFE" and StrokeThickness="3"

    You can create a customLabel control like following code.

      public class MyCustomLabel:Label
        {
    
        
            public static readonly BindableProperty StrokeColorProperty = BindableProperty.CreateAttached("StrokeColor", typeof(string), typeof(MyCustomLabel), "");
            public string StrokeColor
            {
                get { return base.GetValue(StrokeColorProperty).ToString(); }
                set { base.SetValue(StrokeColorProperty, value); }
            }
    
            public static readonly BindableProperty StrokeThicknessProperty = BindableProperty.CreateAttached("StrokeThickness", typeof(int), typeof(MyCustomLabel), 0);
            public int StrokeThickness
            {
                get { return (int) base.GetValue(StrokeThicknessProperty); }
                set { base.SetValue(StrokeThicknessProperty, value); }
            }
        }
    

    Then used it in the xaml folder.

     <labelupdatedemo:MyCustomLabel 
                x:Name = "BTTextStatus"
                StrokeColor="#0FBEFE"
                StrokeThickness="5"
                HorizontalOptions="CenterAndExpand"
                VerticalOptions="CenterAndExpand"
                TextColor="#0B1751"
                FontSize="Medium"/>
    

    In the Android custom renderer like following code.

    [assembly: ExportRenderer(typeof(MyCustomLabel), typeof(MyLabelRenderer))]
    namespace LabelUpdateDemo.Droid
    {
       public class MyLabelRenderer: LabelRenderer
        {
            Context context;
            public MyLabelRenderer(Context context) : base(context) {
                this.context = context;
            }
    
    
    
    
            protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
            {
                base.OnElementChanged(e);
    
                MyCustomLabel  customLabel=(MyCustomLabel) Element;
                var StrokeTextViewColor= customLabel.StrokeColor;
    
                int StrokeThickness = customLabel.StrokeThickness;
                if (Control != null)
                {
    
                    StrokeTextView strokeTextView = new StrokeTextView(context, Control.TextSize, StrokeTextViewColor,  StrokeThickness);
                    strokeTextView.Text = e.NewElement.Text;
                    strokeTextView.SetTextColor(Control.TextColors);
                
                    SetNativeControl(strokeTextView);
                }
            }
    
    
        }
    }
    

    Here is code about StrokeTextView.cs

        class StrokeTextView : TextView
        {
            private TextView borderText = null;
            float OriTextSize;
            public StrokeTextView(Context context,float OriTextSize,string StrokeTextViewColor, int StrokeThickness) : base(context)
            {
                borderText = new TextView(context);
              
                borderText.TextSize = OriTextSize;
                this.TextSize = OriTextSize;
              
                init(StrokeTextViewColor,  StrokeThickness);
            }
            public void init(string StrokeTextViewColor, int StrokeThickness)
            {
                TextPaint tp1 = borderText.Paint;
                tp1.StrokeWidth = StrokeThickness;         // sets the stroke width                        
                tp1.SetStyle(Style.Stroke);
                borderText.SetTextColor(Color.ParseColor(StrokeTextViewColor));  // set the stroke color
                borderText.Gravity = Gravity;
    
            }
    
         
            public override ViewGroup.LayoutParams LayoutParameters { get => base.LayoutParameters; set => base.LayoutParameters = value; }
    
            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                string tt = borderText.Text;
    
    
                if (tt == null || !tt.Equals(this.Text))
                {
                    borderText.Text = Text;
                    this.PostInvalidate();
                }
    
                base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
                borderText.Measure(widthMeasureSpec, heightMeasureSpec);
            }
    
            protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
            {
                base.OnLayout(changed, left, top, right, bottom);
                borderText.Layout(left, top, right, bottom);
            }
    
            protected override void OnDraw(Canvas canvas)
            {
                borderText.Draw(canvas);
                base.OnDraw(canvas);
            }
        }
    }
    

    In the IOS, you can use following custom renderer to achieve it.

    [assembly: ExportRenderer(typeof(MyCustomLabel), typeof(MyLabelRenderer))]
    namespace LabelUpdateDemo.iOS
    {
        class MyLabelRenderer: LabelRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
            {
                base.OnElementChanged(e);
    
                MyCustomLabel myCustomLabel= Element as MyCustomLabel;
    
    
                if (Control != null)
                {
                    UIStringAttributes strokeTextAttributes = new UIStringAttributes();
                    // Here is set the StrokeColor
                  
                    strokeTextAttributes.StrokeColor = Color.FromHex(myCustomLabel.StrokeColor).ToUIColor(); ; 
                    //Here is set the StrokeThickness, IOS is diferert from the android, it border is set to the inside the font.
                    strokeTextAttributes.StrokeWidth = -1*myCustomLabel.StrokeThickness;
    
                    Control.AttributedText = new NSAttributedString(Control.Text, strokeTextAttributes);
                    Control.TextColor = UIColor.Black;
                }
            }
        }
       
    
    }