如何在 Xamarin.Forms 内的 Android.Widget.EditText 上实现 Android.Text.ISpannable?

How do I implement Android.Text.ISpannable on a Android.Widget.EditText inside of Xamarin.Forms?

如何在 Xamarin.Forms 内的 Android.Widget.EditText 上实现 Android.Text.ISpannable

我调查了 Previous Post but the native java implementation is very different compared to Xamarin. And I don't know how to code it correctly. I found the ,但没有找到 Android。

目标: 点击文本输入控件:我想执行以下任一操作:

这可能吗?

此外,link 对应 repository 有问题。请参阅 android 和 iOS 上的“EntryCellRender”。

public class EntryCell :  : CellBaseView, Android.Text.ITextWatcher, Android.Widget.TextView.IOnFocusChangeListener, Android.Widget.TextView.IOnEditorActionListener
{
    AiEditText _EditText;
    ...
    void EntryCell_Focused( object sender, EventArgs e )
    {
        _EditText.RequestFocus();

        // 
        switch ( _EntryCell.OnSelectAction )
        {
            case AiEntryCell.SelectAction.None:
                break;

            case AiEntryCell.SelectAction.Start:
                Selection.SetSelection(_EditText, 0); // Xamarin requires Android.Text.ISpannable implementation.

                break;

            case AiEntryCell.SelectAction.End:
                int position = _EditText.Length();
                Selection.SetSelection(_EditText, position); // Xamarin requires Android.Text.ISpannable implementation.

                break;

            case AiEntryCell.SelectAction.All:
                _EditText.SelectAll();

                break;

            default: throw new ArgumentOutOfRangeException();
        }

        ShowKeyboard(_EditText);
    }

    ...
}

AiEditText的实现

[Android.Runtime.Preserve(AllMembers = true)]
internal class AiEditText : Android.Widget.EditText, Android.Text.ISpannable
{
    public System.Action ClearFocusAction { get; set; }

    public AiEditText( Context context ) : base(context) { }

    public override bool OnKeyPreIme( Keycode keyCode, KeyEvent e )
    {
        if ( keyCode != Keycode.Back ||
             e.Action != KeyEventActions.Up ) return base.OnKeyPreIme(keyCode, e);

        ClearFocus();
        ClearFocusAction?.Invoke();

        return base.OnKeyPreIme(keyCode, e);
    }

    public void RemoveSpan( Java.Lang.Object what )
    {
        throw new NotImplementedException();
    }
    public void SetSpan( Java.Lang.Object what,
                         int start,
                         int end,
                         [GeneratedEnum] Android.Text.SpanTypes flags )
    {
        switch ( flags )
        {
            case SpanTypes.Composing: break;

            case SpanTypes.ExclusiveExclusive: break;

            case SpanTypes.ExclusiveInclusive: break;

            case SpanTypes.InclusiveExclusive: break;

            case SpanTypes.InclusiveInclusive: break;

            case SpanTypes.Intermediate: break;

            case SpanTypes.Paragraph: break;

            case SpanTypes.Priority: break;

            case SpanTypes.PriorityShift: break;

            case SpanTypes.User: break;

            case SpanTypes.UserShift: break;

            default: throw new ArgumentOutOfRangeException(nameof(flags), flags, null);
        }
    }
    public int GetSpanEnd( Java.Lang.Object tag )
    {
        throw new NotImplementedException();
    }
    [return: GeneratedEnum]
    public SpanTypes GetSpanFlags( Java.Lang.Object tag )
    {
        throw new NotImplementedException();
    }
    public Java.Lang.Object[] GetSpans( int start, int end, Class type )
    {
        throw new NotImplementedException();
    }
    public int GetSpanStart( Java.Lang.Object tag )
    {
        throw new NotImplementedException();
    }
    public int NextSpanTransition( int start, int limit, Class type )
    {
        throw new NotImplementedException();
    }
    public char CharAt( int index ) => Text[index];
    public ICharSequence SubSequenceFormatted( int start, int end )
    {
        throw new NotImplementedException();
    }
    public IEnumerator<char> GetEnumerator() { return Text.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

如何使用 custom EntryRenderer 来实现它。

创建自定义条目(MyEntry):

public class MyEntry : Entry
{
}

用于Xaml

<ContentPage ...
    xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
    ...>
    ...
    <local:MyEntry Text="In Shared Code" />
    ...
</ContentPage>

然后在Android中创建自定义渲染器(CustomEntryRenderer)以使用SpannableString.

public class CustomEntryRenderer:EntryRenderer
{
    public CustomEntryRenderer(Context context) : base(context)
    {
    }

    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            //Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);

            SpannableString ss = new SpannableString("green call bold deleteline underline Italic BackgroundColorSpan");

            //color of font
            ss.SetSpan(new ForegroundColorSpan(Android.Graphics.Color.Green), 0, 5, SpanTypes.ExclusiveExclusive);

            //telephone- link
            ss.SetSpan(new URLSpan("tel:4155551212"), 6, 10, SpanTypes.ExclusiveExclusive);

            //bold
            ss.SetSpan(new StyleSpan(TypefaceStyle.Bold), 11, 15, SpanTypes.ExclusiveExclusive);

            //deleteline
            ss.SetSpan(new StrikethroughSpan(), 16, 26, SpanTypes.ExclusiveExclusive);

            //underline
            ss.SetSpan(new UnderlineSpan(), 27, 36, SpanTypes.ExclusiveExclusive);
            ss.SetSpan(new ForegroundColorSpan(Android.Graphics.Color.Red), 25, 34, SpanTypes.ExclusiveExclusive);

            //image
            //Drawable d = Resources.GetDrawable(Resource.Drawable.notification_template_icon_low_bg);
            //d.SetBounds(0, 0, d.IntrinsicWidth, d.IntrinsicHeight);
            //ImageSpan span = new ImageSpan(d,SpanAlign.Baseline);
            //ss.SetSpan(span, 35, 40, SpanTypes.ExclusiveExclusive);  //load image

            //Italic
            ss.SetSpan(new StyleSpan(TypefaceStyle.Italic), 37, 43, SpanTypes.ExclusiveExclusive);
            //BackgroundColorSpan
            ss.SetSpan(new BackgroundColorSpan(Android.Graphics.Color.Yellow), 44, 63, SpanTypes.ExclusiveExclusive);


            //text worked in EditText 
            Control.SetText(ss,TextView.BufferType.Editable);
            Control.MovementMethod = LinkMovementMethod.Instance;
        }
    }
}

效果:

点击进入单元格操作适用于 android。

public class EntryCell :  : CellBaseView, Android.Text.ITextWatcher, Android.Widget.TextView.IOnFocusChangeListener, Android.Widget.TextView.IOnEditorActionListener
{
    AiEditText _EditText;
    ...
    public EntryCellView( Context context, Cell cell ) : base(context, cell)
    {
        ...
        Click += EntryCellView_Click;
        _EditText.Click += EditTextOnClick;
        ...
    }
    
    private void EditTextOnClick( object sender, EventArgs e ) { PerformSelectAction(); }

    private void EntryCellView_Click( object sender, EventArgs e )
    {
        _EditText.RequestFocus();
        PerformSelectAction();
        ShowKeyboard(_EditText); // EntryCellView_Click
    }
    protected void PerformSelectAction()
    {
        _EditText.Cell = _EntryCell;
        switch ( _EntryCell.OnSelectAction )
        {
            case AiEntryCell.SelectAction.None: break;

            case AiEntryCell.SelectAction.Start:
                _EditText.SetSelection(0, 0);

                break;

            case AiEntryCell.SelectAction.End:
                _EditText.SetSelection(_EditText.Length(), _EditText.Length());

                break;

            case AiEntryCell.SelectAction.All:
                _EditText.SelectAll();

                break;

            default: throw new ArgumentOutOfRangeException();
        }
    }
    ...
}