在Xamarin MVVMCross中为iOS/Android通过MvxValueConverter绑定富文本点击命令
iOS 平台实现方案
1. 替换显示控件为 UITextView
UILabel默认不支持富文本点击交互,将TableViewCell中的文本显示控件替换为UITextView,并配置为类Label样式:
_textView = new UITextView(); _textView.Editable = false; // 禁止编辑 _textView.Selectable = true; // 允许选择(支持链接点击) _textView.ScrollEnabled = false; // 禁止滚动,保持Label外观 _textView.BackgroundColor = UIColor.Clear; // 透明背景
2. 扩展富文本转换器,添加链接标识
修改你的FreeShippingConverter,为「Perks Members」添加自定义链接标识(用自定义URL scheme区分),同时设置样式:
public class FreeShippingConverter : MvxValueConverter<string, NSAttributedString> { protected override NSAttributedString Convert(string value, Type targetType, object parameter, CultureInfo culture) { var mutableAttrStr = new NSMutableAttributedString(value); var targetRange = mutableAttrStr.MutableString.LocalizedStandardRangeOfString("Perks Members"); if (targetRange.Location != NSNotFound) { // 添加自定义链接标识 mutableAttrStr.AddAttribute(NSAttributedStringKey.Link, new NSUrl("perks://members"), targetRange); // 设置链接样式 mutableAttrStr.AddAttribute(NSAttributedStringKey.ForegroundColor, UIColor.SystemBlueColor, targetRange); mutableAttrStr.AddAttribute(NSAttributedStringKey.UnderlineStyle, NSNumber.FromInt32((int)NSUnderlineStyle.Single), targetRange); } return mutableAttrStr; } }
3. 处理链接点击,绑定ViewModel命令
为UITextView设置自定义Delegate,拦截链接点击事件并触发ViewModel中的Command:
public class CustomTableViewCell : MvxTableViewCell { public ICommand PerksMembersTappedCommand { get; set; } private UITextView _textView; public CustomTableViewCell(IntPtr handle) : base(handle) { // 初始化_textView(省略布局约束) _textView.Delegate = new PerksLinkTextViewDelegate(this); this.DelayBind(() => { var bindingSet = this.CreateBindingSet<CustomTableViewCell, YourCellViewModel>(); // 绑定富文本 bindingSet.Bind(_textView).For(v => v.AttributedText) .To(vm => vm.ShippingInfoText) .WithConversion<FreeShippingConverter>(); // 绑定点击命令 bindingSet.Bind(this).For(v => v.PerksMembersTappedCommand) .To(vm => vm.PerksMembersTappedCommand); bindingSet.Apply(); }); } private class PerksLinkTextViewDelegate : UITextViewDelegate { private readonly CustomTableViewCell _cell; public PerksLinkTextViewDelegate(CustomTableViewCell cell) => _cell = cell; public override bool ShouldInteractWith(UITextView textView, NSUrl url, NSRange characterRange) { if (url.AbsoluteString.Equals("perks://members", StringComparison.OrdinalIgnoreCase)) { _cell.PerksMembersTappedCommand?.Execute(null); return false; // 阻止系统默认跳转行为 } return true; } } }
Android 平台实现方案
1. 创建可绑定的ClickableSpan
自定义ClickableSpan,持有ViewModel的Command引用,点击时触发命令:
public class CommandClickableSpan : ClickableSpan { private readonly ICommand _command; private readonly Color _linkColor; public CommandClickableSpan(ICommand command, Color linkColor) { _command = command; _linkColor = linkColor; } public override void OnClick(View widget) { _command?.Execute(null); } public override void UpdateDrawState(TextPaint ds) { base.UpdateDrawState(ds); ds.Color = _linkColor; ds.UnderlineText = true; // 显示下划线标识链接 } }
2. 自定义绑定(推荐方案)
创建自定义绑定,直接将TextView的链接点击与ViewModel命令关联,无需修改原有转换器:
第一步:实现自定义绑定类
public class TextViewPerksLinkBinding : MvxAndroidTargetBinding { private readonly TextView _textView; private ICommand _tappedCommand; public TextViewPerksLinkBinding(TextView textView) : base(textView) { _textView = textView; // 启用链接交互 _textView.MovementMethod = LinkMovementMethod.Instance; // 移除点击高亮背景 _textView.HighlightColor = Color.Transparent; } public override Type TargetType => typeof(ICommand); protected override void SetValueImpl(object target, object value) { _tappedCommand = value as ICommand; RefreshLinkSpan(); } // 监听文本变化,自动更新链接Span public override void SubscribeToEvents() { _textView.TextChanged += TextView_TextChanged; } private void TextView_TextChanged(object sender, Android.Text.TextChangedEventArgs e) { RefreshLinkSpan(); } private void RefreshLinkSpan() { var text = _textView.Text; if (string.IsNullOrEmpty(text)) return; var spannable = new SpannableString(text); var startIndex = text.IndexOf("Perks Members", StringComparison.OrdinalIgnoreCase); if (startIndex != -1) { var endIndex = startIndex + "Perks Members".Length; // 移除旧的Span,避免重复添加 var oldSpans = spannable.GetSpans(0, spannable.Length, Java.Lang.Class.FromType(typeof(ClickableSpan))); foreach (var span in oldSpans) { spannable.RemoveSpan(span); } // 添加新的可点击Span spannable.SetSpan(new CommandClickableSpan(_tappedCommand, Color.ParseColor("#007AFF")), startIndex, endIndex, SpanTypes.ExclusiveExclusive); _textView.TextFormatted = spannable; } } protected override void Dispose(bool isDisposing) { if (isDisposing) { _textView.TextChanged -= TextView_TextChanged; } base.Dispose(isDisposing); } public override MvxBindingMode DefaultMode => MvxBindingMode.OneWay; }
第二步:注册自定义绑定
在Setup类中添加绑定注册:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry) { base.FillTargetFactories(registry); registry.RegisterCustomBindingFactory<TextView>("PerksMembersCommand", textView => new TextViewPerksLinkBinding(textView)); }
第三步:布局中绑定
在RecyclerView的Item布局axml中,同时绑定文本和命令:
<TextView android:id="@+id/tv_shipping_info" android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Text ShippingInfoText; PerksMembersCommand PerksMembersTappedCommand" />
内容的提问来源于stack exchange,提问作者Manthan
相关产品推荐
相关产品推荐

