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"/>
Thanks in advance.
Do you want to achieve the result like following screenshot?
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;
}
}
}
}