如何在MAUI中创建带手势事件的自定义Span渲染器(仅安卓和iOS)
实现.NET 7 MAUI带手势的自定义Span渲染器(仅安卓/iOS)
第一步:创建跨平台自定义Span控件
在共享项目中定义带点击事件的自定义Span,作为跨平台抽象层:
using Microsoft.Maui.Controls; namespace YourAppNamespace { public class TapGestureSpan : Span { public event EventHandler Tapped; public void OnTapped() { Tapped?.Invoke(this, EventArgs.Empty); } } }
第二步:安卓平台渲染器实现
在安卓项目的Platforms/Android目录下创建渲染器,通过原生ClickableSpan处理触摸逻辑:
using Android.Text; using Android.Views; using Microsoft.Maui.Controls.Compatibility.Platform.Android; using YourAppNamespace; using YourAppNamespace.Platforms.Android; [assembly: ExportRenderer(typeof(TapGestureSpan), typeof(TapGestureSpanRenderer))] namespace YourAppNamespace.Platforms.Android { public class TapGestureSpanRenderer : SpanRenderer { protected override void OnElementChanged(ElementChangedEventArgs<Span> e) { base.OnElementChanged(e); if (e.NewElement is TapGestureSpan tapSpan && Control is ISpannable spannable) { var clickableSpan = new CustomClickableSpan(tapSpan); spannable.SetSpan(clickableSpan, 0, tapSpan.Text.Length, SpanTypes.ExclusiveExclusive); } } private class CustomClickableSpan : Android.Text.Style.ClickableSpan { private readonly TapGestureSpan _tapSpan; public CustomClickableSpan(TapGestureSpan tapSpan) { _tapSpan = tapSpan; } public override void OnClick(View widget) { _tapSpan.OnTapped(); } public override void UpdateDrawState(TextPaint ds) { base.UpdateDrawState(ds); // 可自定义样式,比如取消默认下划线 ds.UnderlineText = false; } } } }
第三步:iOS平台渲染器实现
在iOS项目的Platforms/iOS目录下创建渲染器,利用UITapGestureRecognizer识别点击位置:
using Foundation; using Microsoft.Maui.Controls.Compatibility.Platform.iOS; using UIKit; using YourAppNamespace; using YourAppNamespace.Platforms.iOS; [assembly: ExportRenderer(typeof(TapGestureSpan), typeof(TapGestureSpanRenderer))] namespace YourAppNamespace.Platforms.iOS { public class TapGestureSpanRenderer : SpanRenderer { private UITapGestureRecognizer _tapGesture; protected override void OnElementChanged(ElementChangedEventArgs<Span> e) { base.OnElementChanged(e); if (e.NewElement is TapGestureSpan tapSpan && Control is NSMutableAttributedString attributedString) { var range = new NSRange(0, attributedString.Length); attributedString.AddAttribute(UIStringAttributeKey.Link, new NSString("tap://"), range); if (_tapGesture == null) { _tapGesture = new UITapGestureRecognizer(HandleTap); ContainerView.AddGestureRecognizer(_tapGesture); } } else if (e.OldElement != null) { // 清理资源 if (_tapGesture != null) { ContainerView.RemoveGestureRecognizer(_tapGesture); _tapGesture.Dispose(); _tapGesture = null; } } } private void HandleTap(UITapGestureRecognizer gesture) { if (Element is TapGestureSpan tapSpan && ContainerView is UITextView textView) { var location = gesture.LocationInView(textView); var position = textView.GetPosition(textView.BeginningOfDocument, location); if (position != null) { var range = textView.GetCharacterRange(position); if (range.Location >= 0 && range.Location < tapSpan.Text.Length) { tapSpan.OnTapped(); } } } } } }
第四步:在页面中使用自定义Span
在XAML中引用自定义Span并绑定点击事件:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:YourAppNamespace" x:Class="YourAppNamespace.MainPage"> <ScrollView> <VerticalStackLayout Padding="10"> <Label FontSize="18"> <Label.FormattedText> <FormattedString> <Span Text="这是一段普通文本," /> <local:TapGestureSpan Text="点击此处触发事件" Tapped="TapGestureSpan_Tapped" /> <Span Text=",继续浏览内容。" /> </FormattedString> </Label.FormattedText> </Label> </VerticalStackLayout> </ScrollView> </ContentPage>
后台代码处理点击事件:
namespace YourAppNamespace { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } private void TapGestureSpan_Tapped(object sender, EventArgs e) { DisplayAlert("提示", "自定义Span被点击!", "确定"); } } }
关键注意事项
- 安卓端需确保包含自定义Span的
Label父容器设置InputTransparent="False",否则手势无法响应。 - iOS端可通过添加
UIStringAttributeKey.ForegroundColor等属性,自定义点击Span的文字样式。 - 非安卓/iOS平台上,该Span会自动降级为普通Span,不具备手势功能。
内容的提问来源于stack exchange,提问作者Mehmet Sandıklı
相关产品推荐
相关产品推荐

