将降价文本加载到 Xamarin.Forms 标签中
Load markdown text into the Xamarin.Forms Label
最近,Xamarin.Forms 标签支持 FormattedText。所以我想使用那个 FormattedText 属性 来加载降价文本。例如,
我有以下几种降价文字,
1. **Hi**, How are you?
2. Hello **John**, _Good Morning_
我想自动将上面的降价文本转换为 FormattedString 以设置为 Label.FormattedText。
谁能帮我实现这个目标?
注意:我不想使用第三方 MarkdownView 控件,因为它们是重量级控件并且在渲染 UI 时存在一些问题检查 Xamarin.Forms iOS.
最后,我自己写了解析,将markdown文本转换成FormattedString。
public static FormattedString GetFormattedString(this string text, double defaultFontSize)
{
var boldFormat = "**";
var italicFormat = "_";
var formatString = new FormattedString();
var temp = text;
while(!string.IsNullOrWhiteSpace(temp))
{
try
{
var boldIndex = temp.IndexOf(boldFormat);
var italicIndex = temp.IndexOf(italicFormat);
if (italicIndex >= 0 && (italicIndex < boldIndex || boldIndex < 0))
{
if (italicIndex > 0)
{
var t = temp.Substring(0, italicIndex);
formatString.Spans.Add(new Span() { Text = t });
}
temp = temp.Substring(italicIndex + 1);
var next = temp.IndexOf(italicFormat);
var t1 = temp.Substring(0, next);
formatString.Spans.Add(new Span() { Text = t1, FontAttributes = FontAttributes.Italic, FontSize = defaultFontSize });
temp = temp.Substring(next + 1);
}
else if (boldIndex >= 0)
{
if (boldIndex > 0)
{
var t = temp.Substring(0, boldIndex);
formatString.Spans.Add(new Span() { Text = t });
}
temp = temp.Substring(boldIndex + 2);
var next = temp.IndexOf(boldFormat);
var t1 = temp.Substring(0, next);
formatString.Spans.Add(new Span() { Text = t1, FontAttributes = FontAttributes.Bold, FontSize = defaultFontSize });
temp = temp.Substring(next + 2);
}
else
{
formatString.Spans.Add(new Span() { Text = temp, FontSize = defaultFontSize });
break;
}
}
catch (Exception)
{
formatString = new FormattedString();
formatString.Spans.Add(new Span() { Text = text, FontSize = defaultFontSize });
break;
}
}
return formatString;
}
注意:目前,我只添加了代码粗体和斜体格式。需要扩展它以获得所需的降价格式。
这是一个示例,说明如何为 Microsoft.Toolkit.Parsers 编写自定义渲染器,以便将一些基本的 Markdown 转换为 Xamarin 标签格式文本:
class LabelMarkdownRenderer : MarkdownRendererBase
{
private readonly Stack<MarkdownInlineType> _inlineTypeStack;
private readonly IDictionary<int, Style> _headerStyles;
private readonly Color _urlLinkColor;
private int _headerLevel;
public LabelMarkdownRenderer(
MarkdownDocument document,
Color urlLinkColor,
IDictionary<int, Style> headerStyles) : base(document)
{
_inlineTypeStack = new Stack<MarkdownInlineType>();
_headerStyles = headerStyles;
_urlLinkColor = urlLinkColor;
}
protected override void RenderParagraph(ParagraphBlock element, IRenderContext context)
{
if (element.Inlines.Any())
{
if (element.Inlines.Any())
{
RenderInlineChildren(element.Inlines, context);
}
if (context.Parent is FormattedString fs)
{
if (fs.Spans?.Any() ?? false)
{
fs.Spans.Last().Text += Environment.NewLine;
}
}
}
}
protected override void RenderYamlHeader(YamlHeaderBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHeader(HeaderBlock element, IRenderContext context)
{
_headerLevel = element.HeaderLevel;
RenderInlineChildren(element.Inlines, context);
if (context.Parent is FormattedString fs)
{
if (fs.Spans?.Any() ?? false)
{
fs.Spans.Last().Text += Environment.NewLine;
}
}
_headerLevel = 0;
}
protected override void RenderListElement(ListBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHorizontalRule(IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderQuote(QuoteBlock element, IRenderContext context)
{
throw new NotImplementedException();
}
protected override void RenderCode(CodeBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderTable(TableBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderEmoji(EmojiInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderTextRun(TextRunInline element, IRenderContext context)
{
if (context.Parent is FormattedString fs)
{
var span = new Span
{
Text = element.Text.Replace("\n\r", Environment.NewLine)
};
if (_headerLevel > 0)
{
span.Style = _headerStyles[_headerLevel];
}
foreach (var inlineType in _inlineTypeStack)
{
switch (inlineType)
{
case MarkdownInlineType.Comment:
case MarkdownInlineType.TextRun:
break;
case MarkdownInlineType.Bold:
span.FontAttributes += (int)FontAttributes.Bold;
break;
case MarkdownInlineType.Italic:
span.FontAttributes += (int)FontAttributes.Italic;
break;
case MarkdownInlineType.MarkdownLink:
break;
case MarkdownInlineType.RawHyperlink:
break;
case MarkdownInlineType.RawSubreddit:
break;
case MarkdownInlineType.Strikethrough:
span.TextDecorations += (int)TextDecorations.Strikethrough;
break;
case MarkdownInlineType.Superscript:
break;
case MarkdownInlineType.Subscript:
break;
case MarkdownInlineType.Code:
break;
case MarkdownInlineType.Image:
break;
case MarkdownInlineType.Emoji:
break;
case MarkdownInlineType.LinkReference:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
fs.Spans.Add(span);
}
}
protected override void RenderBoldRun(BoldTextInline element, IRenderContext context)
{
RenderInlineType(element.Inlines, MarkdownInlineType.Bold, context);
}
protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context)
{
var text = string.Join(string.Empty, element.Inlines);
RenderLink(text, element.Url, context);
}
protected override void RenderImage(ImageInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHyperlink(HyperlinkInline element, IRenderContext context)
{
RenderLink(element.Text, element.Url, context);
}
protected override void RenderItalicRun(ItalicTextInline element, IRenderContext context)
{
RenderInlineType(element.Inlines, MarkdownInlineType.Italic, context);
}
protected override void RenderStrikethroughRun(StrikethroughTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderSuperscriptRun(SuperscriptTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderSubscriptRun(SubscriptTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderCodeRun(CodeInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
private void RenderLink(string text, string url, IRenderContext context)
{
if (context.Parent is FormattedString fs)
{
var span = new Span
{
Text = text,
TextDecorations = TextDecorations.Underline,
TextColor = _urlLinkColor
};
var tap = new TapGestureRecognizer
{
Command = new Command<string>(urlStr => Device.OpenUri(new Uri(urlStr))),
CommandParameter = url
};
span.GestureRecognizers.Add(tap);
fs.Spans.Add(span);
}
}
private void RenderInlineType(IList<MarkdownInline> inlines, MarkdownInlineType markdownInlineType, IRenderContext context)
{
_inlineTypeStack.Push(markdownInlineType);
RenderInlineChildren(inlines, context);
_inlineTypeStack.Pop();
}
}
有关更多详细信息以及如何将此代码转换为在 Xamarin Forms 中扩展 Label 的自定义控件,请参阅此项目:https://github.com/1iveowl/plugin.label.markdown/blob/master/src/main/Plugin.Label.MarkDown/Renderer/LabelMarkdownRenderer.cs
最近,Xamarin.Forms 标签支持 FormattedText。所以我想使用那个 FormattedText 属性 来加载降价文本。例如,
我有以下几种降价文字,
1. **Hi**, How are you?
2. Hello **John**, _Good Morning_
我想自动将上面的降价文本转换为 FormattedString 以设置为 Label.FormattedText。
谁能帮我实现这个目标?
注意:我不想使用第三方 MarkdownView 控件,因为它们是重量级控件并且在渲染 UI 时存在一些问题检查 Xamarin.Forms iOS.
最后,我自己写了解析,将markdown文本转换成FormattedString。
public static FormattedString GetFormattedString(this string text, double defaultFontSize)
{
var boldFormat = "**";
var italicFormat = "_";
var formatString = new FormattedString();
var temp = text;
while(!string.IsNullOrWhiteSpace(temp))
{
try
{
var boldIndex = temp.IndexOf(boldFormat);
var italicIndex = temp.IndexOf(italicFormat);
if (italicIndex >= 0 && (italicIndex < boldIndex || boldIndex < 0))
{
if (italicIndex > 0)
{
var t = temp.Substring(0, italicIndex);
formatString.Spans.Add(new Span() { Text = t });
}
temp = temp.Substring(italicIndex + 1);
var next = temp.IndexOf(italicFormat);
var t1 = temp.Substring(0, next);
formatString.Spans.Add(new Span() { Text = t1, FontAttributes = FontAttributes.Italic, FontSize = defaultFontSize });
temp = temp.Substring(next + 1);
}
else if (boldIndex >= 0)
{
if (boldIndex > 0)
{
var t = temp.Substring(0, boldIndex);
formatString.Spans.Add(new Span() { Text = t });
}
temp = temp.Substring(boldIndex + 2);
var next = temp.IndexOf(boldFormat);
var t1 = temp.Substring(0, next);
formatString.Spans.Add(new Span() { Text = t1, FontAttributes = FontAttributes.Bold, FontSize = defaultFontSize });
temp = temp.Substring(next + 2);
}
else
{
formatString.Spans.Add(new Span() { Text = temp, FontSize = defaultFontSize });
break;
}
}
catch (Exception)
{
formatString = new FormattedString();
formatString.Spans.Add(new Span() { Text = text, FontSize = defaultFontSize });
break;
}
}
return formatString;
}
注意:目前,我只添加了代码粗体和斜体格式。需要扩展它以获得所需的降价格式。
这是一个示例,说明如何为 Microsoft.Toolkit.Parsers 编写自定义渲染器,以便将一些基本的 Markdown 转换为 Xamarin 标签格式文本:
class LabelMarkdownRenderer : MarkdownRendererBase
{
private readonly Stack<MarkdownInlineType> _inlineTypeStack;
private readonly IDictionary<int, Style> _headerStyles;
private readonly Color _urlLinkColor;
private int _headerLevel;
public LabelMarkdownRenderer(
MarkdownDocument document,
Color urlLinkColor,
IDictionary<int, Style> headerStyles) : base(document)
{
_inlineTypeStack = new Stack<MarkdownInlineType>();
_headerStyles = headerStyles;
_urlLinkColor = urlLinkColor;
}
protected override void RenderParagraph(ParagraphBlock element, IRenderContext context)
{
if (element.Inlines.Any())
{
if (element.Inlines.Any())
{
RenderInlineChildren(element.Inlines, context);
}
if (context.Parent is FormattedString fs)
{
if (fs.Spans?.Any() ?? false)
{
fs.Spans.Last().Text += Environment.NewLine;
}
}
}
}
protected override void RenderYamlHeader(YamlHeaderBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHeader(HeaderBlock element, IRenderContext context)
{
_headerLevel = element.HeaderLevel;
RenderInlineChildren(element.Inlines, context);
if (context.Parent is FormattedString fs)
{
if (fs.Spans?.Any() ?? false)
{
fs.Spans.Last().Text += Environment.NewLine;
}
}
_headerLevel = 0;
}
protected override void RenderListElement(ListBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHorizontalRule(IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderQuote(QuoteBlock element, IRenderContext context)
{
throw new NotImplementedException();
}
protected override void RenderCode(CodeBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderTable(TableBlock element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderEmoji(EmojiInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderTextRun(TextRunInline element, IRenderContext context)
{
if (context.Parent is FormattedString fs)
{
var span = new Span
{
Text = element.Text.Replace("\n\r", Environment.NewLine)
};
if (_headerLevel > 0)
{
span.Style = _headerStyles[_headerLevel];
}
foreach (var inlineType in _inlineTypeStack)
{
switch (inlineType)
{
case MarkdownInlineType.Comment:
case MarkdownInlineType.TextRun:
break;
case MarkdownInlineType.Bold:
span.FontAttributes += (int)FontAttributes.Bold;
break;
case MarkdownInlineType.Italic:
span.FontAttributes += (int)FontAttributes.Italic;
break;
case MarkdownInlineType.MarkdownLink:
break;
case MarkdownInlineType.RawHyperlink:
break;
case MarkdownInlineType.RawSubreddit:
break;
case MarkdownInlineType.Strikethrough:
span.TextDecorations += (int)TextDecorations.Strikethrough;
break;
case MarkdownInlineType.Superscript:
break;
case MarkdownInlineType.Subscript:
break;
case MarkdownInlineType.Code:
break;
case MarkdownInlineType.Image:
break;
case MarkdownInlineType.Emoji:
break;
case MarkdownInlineType.LinkReference:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
fs.Spans.Add(span);
}
}
protected override void RenderBoldRun(BoldTextInline element, IRenderContext context)
{
RenderInlineType(element.Inlines, MarkdownInlineType.Bold, context);
}
protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context)
{
var text = string.Join(string.Empty, element.Inlines);
RenderLink(text, element.Url, context);
}
protected override void RenderImage(ImageInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderHyperlink(HyperlinkInline element, IRenderContext context)
{
RenderLink(element.Text, element.Url, context);
}
protected override void RenderItalicRun(ItalicTextInline element, IRenderContext context)
{
RenderInlineType(element.Inlines, MarkdownInlineType.Italic, context);
}
protected override void RenderStrikethroughRun(StrikethroughTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderSuperscriptRun(SuperscriptTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderSubscriptRun(SubscriptTextInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
protected override void RenderCodeRun(CodeInline element, IRenderContext context)
{
//throw new NotImplementedException();
}
private void RenderLink(string text, string url, IRenderContext context)
{
if (context.Parent is FormattedString fs)
{
var span = new Span
{
Text = text,
TextDecorations = TextDecorations.Underline,
TextColor = _urlLinkColor
};
var tap = new TapGestureRecognizer
{
Command = new Command<string>(urlStr => Device.OpenUri(new Uri(urlStr))),
CommandParameter = url
};
span.GestureRecognizers.Add(tap);
fs.Spans.Add(span);
}
}
private void RenderInlineType(IList<MarkdownInline> inlines, MarkdownInlineType markdownInlineType, IRenderContext context)
{
_inlineTypeStack.Push(markdownInlineType);
RenderInlineChildren(inlines, context);
_inlineTypeStack.Pop();
}
}
有关更多详细信息以及如何将此代码转换为在 Xamarin Forms 中扩展 Label 的自定义控件,请参阅此项目:https://github.com/1iveowl/plugin.label.markdown/blob/master/src/main/Plugin.Label.MarkDown/Renderer/LabelMarkdownRenderer.cs