Xamarin.Forms 标签中带有 FormattedString 的自定义字体
Custom Font in Xamarin.Forms Label with FormattedString
我在我的 Android 应用程序中创建了自定义 LabelRenderer 以在 Xamarin Android 应用程序 (https://developer.xamarin.com/guides/xamarin-forms/user-interface/text/fonts/) 中应用自定义字体。
对于将内容添加到 .Text 属性 的普通标签,一切都很好。但是,如果我使用 .FormattedText 属性 创建标签,则不会应用自定义字体。
有人成功过吗?一个选项,因为我只是堆叠不同大小的文本行,所以对每个文本使用单独的标签控件,但如果可能的话我更愿意使用格式化字符串。
这是我的自定义渲染器的内容:
[assembly: ExportRenderer (typeof (gbrLabel), typeof (gbrLabelRenderer))]
public class gbrLabelRenderer: LabelRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Label> e)
{
base.OnElementChanged (e);
var label = (TextView)Control;
Typeface font = Typeface.CreateFromAsset (Forms.Context.Assets, "Lobster-Regular.ttf");
label.Typeface = font;
}
}
这是我的简单标签控件...它所做的只是将字体应用到 iOS,并将 Android 的字体应用到自定义渲染器。
public class gbrLabel: Label
{
public gbrLabel ()
{
Device.OnPlatform (
iOS: () => {
FontFamily = "Lobster-Regular";
FontSize = Device.GetNamedSize(NamedSize.Medium,this);
}
}
}
仅适用于带有 .Text 属性... 的标签但不适用于带有 .FormattedText 属性.
的标签
我应该继续挖掘,还是只是堆叠我的标签,因为在这种情况下这是一个选项?
这是我在格式化文本中尝试过的各种方法的示例,因为这是被要求的:
var fs = new FormattedString ();
fs.Spans.Add (new Span {
Text = string.Format("LINE 1\n",Title),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label))
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 2\n"),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label)) * 2,
FontAttributes = FontAttributes.Bold,
FontFamily = "Lobster-Regular"
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 3\n"),
FontSize = Device.GetNamedSize(NamedSize.Medium,typeof(Label)),
FontFamily = "Lobster-Regular.ttf"
});
gbrLabel lblContent = new gbrLabel {
FormattedText = fs
}
None 其中(第一个应该默认设置 class / 渲染器,第二个是在 span 定义本身中包含字体的变体)在 Android.
注: Android 和 iOS 问题已经总结在博客上 post: smstuebe.de/2016/04/03/formattedtext.xamrin.forms/
只要不设置FontSize
或FontAttributes
,字体就设置好了。所以我查看了实现,发现 FormattedText
正在尝试加载字体,就像默认渲染器一样,它在 Android.
上不起作用
android 格式化系统的工作方式与 Xamarin.Forms 中的非常相似。它使用 span 来定义文本属性。渲染器正在为每个 Span
添加一个 FontSpan
,并使用自定义字体、大小或属性。不幸的是,FontSpan
class 是 FormattedStringExtensions
的私有内部 class,因此我们必须处理反射。
我们的渲染器在初始化时和 FormattedText
属性 更改时更新 Control.TextFormatted
。在更新方法中,我们获取所有 FontSpan
并将它们替换为我们的 CustomTypefaceSpan
。
渲染器
public class FormattedLabelRenderer : LabelRenderer
{
private static readonly Typeface Font = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.Typeface = Font;
UpdateFormattedText();
}
private void UpdateFormattedText()
{
if (Element.FormattedText != null)
{
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, 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)
{
UpdateFormattedText();
}
}
}
我不确定,为什么要引入新的元素类型 gbrLabel
,但只要您只想更改渲染器,就不必创建自定义元素。您可以替换默认元素的渲染器:
[assembly: ExportRenderer(typeof(Label), typeof(FormattedLabelRenderer))]
CustomTypefaceSpan
public class CustomTypefaceSpan : MetricAffectingSpan
{
private readonly Typeface _typeFace;
private readonly Typeface _typeFaceBold;
private readonly Typeface _typeFaceItalic;
private readonly Typeface _typeFaceBoldItalic;
private readonly TextView _textView;
private Font _font;
public CustomTypefaceSpan(TextView textView, Font font)
{
_textView = textView;
_font = font;
// Note: we are ignoring _font.FontFamily (but thats easy to change)
_typeFace = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
_typeFaceBold = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Bold.ttf");
_typeFaceItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Italic.ttf");
_typeFaceBoldItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-BoldItalic.ttf");
}
public override void UpdateDrawState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
public override void UpdateMeasureState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
private void ApplyCustomTypeFace(Paint paint)
{
var tf = _typeFace;
if (_font.FontAttributes.HasFlag(FontAttributes.Bold) && _font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceBoldItalic;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Bold))
{
tf = _typeFaceBold;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceItalic;
}
paint.SetTypeface(tf);
paint.TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, _font.ToScaledPixel(), _textView.Resources.DisplayMetrics);
}
}
我们的自定义CustomTypefaceSpan
类似于Xamarin.Forms的FontSpan
,但是加载的是自定义字体,可以为不同的FontAttributes
加载不同的字体。
结果是漂亮的彩色文本 :)
我在我的 Android 应用程序中创建了自定义 LabelRenderer 以在 Xamarin Android 应用程序 (https://developer.xamarin.com/guides/xamarin-forms/user-interface/text/fonts/) 中应用自定义字体。
对于将内容添加到 .Text 属性 的普通标签,一切都很好。但是,如果我使用 .FormattedText 属性 创建标签,则不会应用自定义字体。
有人成功过吗?一个选项,因为我只是堆叠不同大小的文本行,所以对每个文本使用单独的标签控件,但如果可能的话我更愿意使用格式化字符串。
这是我的自定义渲染器的内容:
[assembly: ExportRenderer (typeof (gbrLabel), typeof (gbrLabelRenderer))]
public class gbrLabelRenderer: LabelRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Label> e)
{
base.OnElementChanged (e);
var label = (TextView)Control;
Typeface font = Typeface.CreateFromAsset (Forms.Context.Assets, "Lobster-Regular.ttf");
label.Typeface = font;
}
}
这是我的简单标签控件...它所做的只是将字体应用到 iOS,并将 Android 的字体应用到自定义渲染器。
public class gbrLabel: Label
{
public gbrLabel ()
{
Device.OnPlatform (
iOS: () => {
FontFamily = "Lobster-Regular";
FontSize = Device.GetNamedSize(NamedSize.Medium,this);
}
}
}
仅适用于带有 .Text 属性... 的标签但不适用于带有 .FormattedText 属性.
的标签我应该继续挖掘,还是只是堆叠我的标签,因为在这种情况下这是一个选项?
这是我在格式化文本中尝试过的各种方法的示例,因为这是被要求的:
var fs = new FormattedString ();
fs.Spans.Add (new Span {
Text = string.Format("LINE 1\n",Title),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label))
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 2\n"),
FontSize = Device.GetNamedSize(NamedSize.Large,typeof(Label)) * 2,
FontAttributes = FontAttributes.Bold,
FontFamily = "Lobster-Regular"
});
fs.Spans.Add (new Span {
Text = string.Format ("LINE 3\n"),
FontSize = Device.GetNamedSize(NamedSize.Medium,typeof(Label)),
FontFamily = "Lobster-Regular.ttf"
});
gbrLabel lblContent = new gbrLabel {
FormattedText = fs
}
None 其中(第一个应该默认设置 class / 渲染器,第二个是在 span 定义本身中包含字体的变体)在 Android.
注: Android 和 iOS 问题已经总结在博客上 post: smstuebe.de/2016/04/03/formattedtext.xamrin.forms/
只要不设置FontSize
或FontAttributes
,字体就设置好了。所以我查看了实现,发现 FormattedText
正在尝试加载字体,就像默认渲染器一样,它在 Android.
android 格式化系统的工作方式与 Xamarin.Forms 中的非常相似。它使用 span 来定义文本属性。渲染器正在为每个 Span
添加一个 FontSpan
,并使用自定义字体、大小或属性。不幸的是,FontSpan
class 是 FormattedStringExtensions
的私有内部 class,因此我们必须处理反射。
我们的渲染器在初始化时和 FormattedText
属性 更改时更新 Control.TextFormatted
。在更新方法中,我们获取所有 FontSpan
并将它们替换为我们的 CustomTypefaceSpan
。
渲染器
public class FormattedLabelRenderer : LabelRenderer
{
private static readonly Typeface Font = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.Typeface = Font;
UpdateFormattedText();
}
private void UpdateFormattedText()
{
if (Element.FormattedText != null)
{
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, 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)
{
UpdateFormattedText();
}
}
}
我不确定,为什么要引入新的元素类型 gbrLabel
,但只要您只想更改渲染器,就不必创建自定义元素。您可以替换默认元素的渲染器:
[assembly: ExportRenderer(typeof(Label), typeof(FormattedLabelRenderer))]
CustomTypefaceSpan
public class CustomTypefaceSpan : MetricAffectingSpan
{
private readonly Typeface _typeFace;
private readonly Typeface _typeFaceBold;
private readonly Typeface _typeFaceItalic;
private readonly Typeface _typeFaceBoldItalic;
private readonly TextView _textView;
private Font _font;
public CustomTypefaceSpan(TextView textView, Font font)
{
_textView = textView;
_font = font;
// Note: we are ignoring _font.FontFamily (but thats easy to change)
_typeFace = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Regular.ttf");
_typeFaceBold = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Bold.ttf");
_typeFaceItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-Italic.ttf");
_typeFaceBoldItalic = Typeface.CreateFromAsset(Forms.Context.Assets, "LobsterTwo-BoldItalic.ttf");
}
public override void UpdateDrawState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
public override void UpdateMeasureState(TextPaint paint)
{
ApplyCustomTypeFace(paint);
}
private void ApplyCustomTypeFace(Paint paint)
{
var tf = _typeFace;
if (_font.FontAttributes.HasFlag(FontAttributes.Bold) && _font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceBoldItalic;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Bold))
{
tf = _typeFaceBold;
}
else if (_font.FontAttributes.HasFlag(FontAttributes.Italic))
{
tf = _typeFaceItalic;
}
paint.SetTypeface(tf);
paint.TextSize = TypedValue.ApplyDimension(ComplexUnitType.Sp, _font.ToScaledPixel(), _textView.Resources.DisplayMetrics);
}
}
我们的自定义CustomTypefaceSpan
类似于Xamarin.Forms的FontSpan
,但是加载的是自定义字体,可以为不同的FontAttributes
加载不同的字体。
结果是漂亮的彩色文本 :)