Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to make a label fill the space from left to right?

Here is the code I have:

<StackLayout>
   <Label x:Name="emptyLabel1" FontSize="18" XAlign="Start" TextColor="Gray" />
   <Label x:Name="emptyLabel2" FontSize="18" XAlign="Center" TextColor="Gray" />
   <Label x:Name="emptyLabel3" FontSize="18" XAlign="Center" TextColor="Gray" />
</StackLayout>

The first multi-line label starts on the left but has spaces on some of the rows on the right. The 2nd and 3rd multi-line labels are centered and have spaces on both left and right.

Is there any way that I can have all rows of the labels completely fill the rows completely fill from left to right o that the first character of each row always lines up on the left and the last character of the last word of each row always lines up on the right? Note that this would require some words in each line to have different gaps between them.

like image 614
Alan2 Avatar asked Sep 01 '17 08:09

Alan2


2 Answers

It is a bit tricky to implement label with justify alignment support, but it is possible through platform renderer(s).

First step would be to declare a custom control in forms project.

public class JustifiedLabel : Label { }

Next step is to define and register the platform renderer in iOS. This one is simple, as we simply combine formatted-string with paragraph-style to get what we want.

[assembly: ExportRenderer(typeof(JustifiedLabel), typeof(JustifiedLabelRenderer))]
namespace SomeAppNamespace.iOS
{   
    public class JustifiedLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control
            if (e.NewElement != null)
                UpdateTextOnControl();
        }

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

            //if there is change in text or font-style, trigger update to redraw control
            if(e.PropertyName == nameof(Label.Text) 
               || e.PropertyName == nameof(Label.FontFamily) 
               || e.PropertyName == nameof(Label.FontSize)
               || e.PropertyName == nameof(Label.TextColor)
               || e.PropertyName == nameof(Label.FontAttributes))
            {
                UpdateTextOnControl();
            }
        }

        void UpdateTextOnControl()
        {
            if (Control == null)
                return;

            //define paragraph-style
            var style = new NSMutableParagraphStyle()
            {
                Alignment = UITextAlignment.Justified,
                FirstLineHeadIndent = 0.001f,
            };

            //define attributes that use both paragraph-style, and font-style 
            var uiAttr = new UIStringAttributes()
            {
                ParagraphStyle = style,
                BaselineOffset = 0,

                Font = Control.Font
            };

            //define frame to ensure justify alignment is applied
            Control.Frame = new RectangleF(0, 0, (float)Element.Width, (float)Element.Height);

            //set new text with ui-style-attributes to native control (UILabel)
            var stringToJustify = Control.Text ?? string.Empty;
            var attributedString = new Foundation.NSAttributedString(stringToJustify, uiAttr.Dictionary);
            Control.AttributedText = attributedString;
            Control.Lines = 0;
        }
    }
}

In android platform, it is a bit trickier - as android doesn't support justify alignment for TextView - so we will need to use a WebView instead to get it to render the text.

(Note: You can also alternatively use an android library and use it instead of WebView)

[assembly: ExportRenderer(typeof(JustifiedLabel), typeof(JustifiedLabelRenderer))]
namespace SomeAppNamespace.Droid
{
    //We don't extend from LabelRenderer on purpose as we want to set 
    // our own native control (which is not TextView)
    public class JustifiedLabelRenderer : ViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<View> e)
        {
            base.OnElementChanged(e);

            //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    //register webview as native control
                    var webView = new Android.Webkit.WebView(Context);
                    webView.VerticalScrollBarEnabled = false;
                    webView.HorizontalScrollBarEnabled = false;

                    webView.LoadData("<html><body>&nbsp;</body></html>", "text/html; charset=utf-8", "utf-8");
                    SetNativeControl(webView);
                }

                //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control
                UpdateTextOnControl();
            }   
        }

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

            //if there is change in text or font-style, trigger update to redraw control
            if (e.PropertyName == nameof(Label.Text)
               || e.PropertyName == nameof(Label.FontFamily)
               || e.PropertyName == nameof(Label.FontSize)
               || e.PropertyName == nameof(Label.TextColor)
               || e.PropertyName == nameof(Label.FontAttributes))
            {
                UpdateTextOnControl();
            }
        }

        void UpdateTextOnControl()
        {
            var webView = Control as Android.Webkit.WebView; 
            var formsLabel = Element as Label;

            // create css style from font-style as specified
            var cssStyle = $"margin: 0px; padding: 0px; text-align: justify; color: {ToHexColor(formsLabel.TextColor)}; background-color: {ToHexColor(formsLabel.BackgroundColor)}; font-family: {formsLabel.FontFamily}; font-size: {formsLabel.FontSize}; font-weight: {formsLabel.FontAttributes}";

            // apply that to text 
            var strData =
                $"<html><body style=\"{cssStyle}\">{formsLabel?.Text}</body></html>";

            // and, refresh webview
            webView.LoadData(strData, "text/html; charset=utf-8", "utf-8");
            webView.Reload();
        }

        // helper method to convert forms-color to css-color
        string ToHexColor(Color color)
        {
            var red = (int)(color.R * 255);
            var green = (int)(color.G * 255);
            var blue = (int)(color.B * 255);
            var alpha = (int)(color.A * 255);
            var hex = $"#{red:X2}{green:X2}{blue:X2}";

            return hex;
        }
    }
}

Sample usage

<StackLayout Margin="20">
    <Entry x:Name="InputEntry" />

    <Label Margin="0,10,0,0" BackgroundColor="Navy" TextColor="White" Text="Normal Text Label" FontSize="15" HorizontalOptions="CenterAndExpand" />
    <Label 
            FontSize="20" 
            FontAttributes="Bold"  
            Text="{Binding Text, Source={x:Reference InputEntry}}" />

    <Label Margin="0,10,0,0" BackgroundColor="Navy" TextColor="White" Text="Justified Text Label" FontSize="15" HorizontalOptions="CenterAndExpand" />
    <local:JustifiedLabel 
            FontSize="20" 
            FontAttributes="Bold" 
            Text="{Binding Text, Source={x:Reference InputEntry}}"
            TextColor="Green"
            BackgroundColor="Yellow"
            VerticalOptions="FillAndExpand"
            HorizontalOptions="FillAndExpand" />

</StackLayout>

enter image description here enter image description here

like image 110
Sharada Gururaj Avatar answered Oct 29 '22 04:10

Sharada Gururaj


I think you can try with

HorizontalOptions=LayoutOptions.FillAndExpand

HorizontalOptions

like image 4
Alessandro Caliaro Avatar answered Oct 29 '22 03:10

Alessandro Caliaro