Do custom fonts work for formatted string?

寵の児 提交于 2019-12-11 11:09:51

问题


I have followed Sven's example for custom font for formattedstrings here: https://github.com/smstuebe/xamarin-forms-formattedtext

However, I'm running into a strange issue where the tap gestures on my view don't work if I implement the UIFormattedStringLabel(). This is super strange because if I use a regular label that does not use the custom renderer provided, the gestures are detected, the formatted string is displayed (just the fonts are default) and everything loads correctly.

So, I think there's an issue with setting up the renderer above as the problem seems to stem from that. Perhaps, I am missing a step for what I am trying to do.

using Android.Graphics;
using Android.Text;
using Android.Text.Style;
using Android.Util;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

namespace Proj.Droid.CustomRenderer
{
    public class CustomTypefaceSpan : MetricAffectingSpan
    {
        private readonly Typeface _typeFace;
        private readonly TextView _textView;
        private Font _font;

        public CustomTypefaceSpan(TextView textView, Label label, Font font)
        {
            _textView = textView;
            _font = font;
            _typeFace = Typeface.CreateFromAsset(Forms.Context.Assets, GetFontName(_font.FontFamily ?? label.FontFamily, _font.FontAttributes));
        }

        private static string GetFontName(string fontFamily, FontAttributes fontAttributes)
        {
            var postfix = "Regular";
            var bold = fontAttributes.HasFlag(FontAttributes.Bold);
            var italic = fontAttributes.HasFlag(FontAttributes.Italic);
            if (bold && italic) { postfix = "BoldItalic"; }
            else if (bold) { postfix = "Bold"; }
            else if (italic) { postfix = "Italic"; }

            return $"{fontFamily}-{postfix}.otf";
        }

        public override void UpdateDrawState(TextPaint paint)
        {
            ApplyCustomTypeFace(paint);
        }

        public override void UpdateMeasureState(TextPaint paint)
        {
            ApplyCustomTypeFace(paint);
        }

        private void ApplyCustomTypeFace(Paint paint)
        {
            paint.SetTypeface(_typeFace);
            paint.TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, _font.ToScaledPixel(), _textView.Resources.DisplayMetrics);
        }
    }
}

using System.ComponentModel;
using System.Reflection;
using Android.Graphics;
using Android.Text;
using Java.Lang;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Proj.Droid.CustomRenderer;
using Proj.Core.UI.XamarinForms.Controls;
using System;

[assembly: ExportRenderer(typeof(UIFormattedStringLabel), typeof(FormattedLabelRenderer))]
namespace Proj.Droid.CustomRenderer
{
    public class SimpleLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                Control.Typeface = Typeface.CreateFromAsset(Forms.Context.Assets, GetFontName(Element.FontFamily, Element.FontAttributes));
            }
        }

        private static string GetFontName(string fontFamily, FontAttributes fontAttributes)
        {
            var postfix = "Regular";
            var bold = fontAttributes.HasFlag(FontAttributes.Bold);
            var italic = fontAttributes.HasFlag(FontAttributes.Italic);
            if (bold && italic) { postfix = "BoldItalic"; }
            else if (bold) { postfix = "Bold"; }
            else if (italic) { postfix = "Italic"; }

            return $"{fontFamily}-{postfix}.otf";
        }
    }



     public class FormattedLabelRenderer : SimpleLabelRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
            {
                base.OnElementChanged(e);
                UpdateFormattedText();
            }

            private void UpdateFormattedText()
            {
                if (Element?.FormattedText == null)
                    return;

                var extensionType = typeof(FormattedStringExtensions);
                var type = extensionType.GetNestedType("FontSpan", BindingFlags.NonPublic);
                var ss = new SpannableString(Control.TextFormatted);
                var spans = ss.GetSpans(0, ss.ToString().Length, Class.FromType(type));
                foreach (var span in spans)
                {
                    var start = ss.GetSpanStart(span);
                    var end = ss.GetSpanEnd(span);
                    var flags = ss.GetSpanFlags(span);
                    var font = (Font)type.GetProperty("Font").GetValue(span, null);
                    ss.RemoveSpan(span);
                    var newSpan = new CustomTypefaceSpan(Control, Element, font);
                    ss.SetSpan(newSpan, start, end, flags);
                }
                Control.TextFormatted = ss;
            }

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

                if (e.PropertyName == Label.FormattedTextProperty.PropertyName ||
                    e.PropertyName == Label.TextProperty.PropertyName ||
                    e.PropertyName == Label.FontAttributesProperty.PropertyName ||
                    e.PropertyName == Label.FontProperty.PropertyName ||
                    e.PropertyName == Label.FontSizeProperty.PropertyName ||
                    e.PropertyName == Label.FontFamilyProperty.PropertyName ||
                    e.PropertyName == Label.TextColorProperty.PropertyName)
                {
                    UpdateFormattedText();
                }
            }
        }
    }

using System;
using Xamarin.Forms;
using XLabs.Forms.Controls;

namespace Proj.Core.UI.XamarinForms.Controls
{

    public class UIFormattedStringLabel : Label
    {
        public UIFormattedStringLabel()
        {

        }
    }
}

label code:

var formatString = new FormattedString();

formatString.Spans.Add(new Span { Text = Time.Text + "\n", FontAttributes = FontAttributes.Bold, ForegroundColor = ColorHelper.FromHex(CoreTheme.COLOR_DARK_GREY)});
formatString.Spans.Add(new Span { Text = TimeRemaining.Text, FontAttributes = FontAttributes.Bold,ForegroundColor = ColorHelper.FromHex(CoreTheme.COLOR_DEFAULT_BLACK)});

labelTime = new UIFormattedStringLabel(); 
labelTime.ClassId = offerid.ToString();
labelTime.WidthRequest = (DeviceDisplaySettings.defaultwidth / buttonsToShow) - 10;
labelTime.HeightRequest = 40;
labelTime.VerticalTextAlignment = TextAlignment.Center;
labelTime.BackgroundColor = ColorHelper.FromHex(CoreTheme.COLOR_LIGHT_GREY);
labelTime.FormattedText = formatString; 

回答1:


As mentioned in my blogpost, the Renderer isn't perfect. In your case it will crash, if the FontFamily of the Label is null and the FontFamily of the Span is null.

I added some fixes for this to the repository. https://github.com/smstuebe/xamarin-forms-formattedtext/commit/d3b9eab7f588917f1e4417188a12e66f97cf1081

UpdateFormattedText

We only replace the span, if we have a font set.

private void UpdateFormattedText()
{
    if (Element?.FormattedText == null)
        return;

    var extensionType = typeof(FormattedStringExtensions);
    var type = extensionType.GetNestedType("FontSpan", BindingFlags.NonPublic);
    var ss = new SpannableString(Control.TextFormatted);
    var spans = ss.GetSpans(0, ss.ToString().Length, Class.FromType(type));
    foreach (var span in spans)
    {
        var font = (Font)type.GetProperty("Font").GetValue(span, null);
        if ((font.FontFamily ?? Element.FontFamily) != null)
        {
            var start = ss.GetSpanStart(span);
            var end = ss.GetSpanEnd(span);
            var flags = ss.GetSpanFlags(span);
            ss.RemoveSpan(span);
            var newSpan = new CustomTypefaceSpan(Control, Element, font);
            ss.SetSpan(newSpan, start, end, flags);
        }
    }
    Control.TextFormatted = ss;
}

I don't check for null in CustomTypefaceSpan.ApplyCustomTypeFace(Paint paint), because it will show you that your font got not loaded correctly, and you have to double check the names and the assets.




回答2:


Typeface is set using the SetTypeface method. Changing Renderer as below might fix it

class FormattedStringLabelRender : LabelRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
    {
        base.OnElementChanged(e);
        var label = (TextView)Control; // for example
        Typeface font = Typeface.CreateFromAsset(Forms.Context.Assets, "Fonts/ProximaNova-Bold.otf");  // font name specified here
        label.SetTypeface (font, TypefaceStyle.Normal);
    }
}


来源:https://stackoverflow.com/questions/36743751/do-custom-fonts-work-for-formatted-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!