使用退格按钮专注于 Xamarin 中的上一个输入字段

问题描述

在我的 Xamarin 应用程序的 PIN 页面上,有 8 个输入字段,4 个用于 PIN,4 个用于确认 PIN(这里我只粘贴 2 个)。

用户一个字段中输入一个值时,光标会聚焦在下一个字段上,依此类推。但是如果他们错误地输入了值并想要清除它,他们不能通过按退格按钮返回到最后一个条目。他们必须按下那个输入字段才能获得焦点。

以下是输入字段的代码。如何通过按 Backspace 按钮返回/关注最后一个输入字段?

.xml

<Entry
    x:Name="PasswordOne"
    Text="{Binding PasswordOne}"
    IsPassword="True"
    Keyboard="Numeric"
    MaxLength="1"
    ReturnType="Next"
    TextChanged="Entry_FieldChanged" />

.xml.cs

public PasswordPage()
{
    InitializeComponent();

    entryList = new List<Entry>();
    entryList.Add(PasswordOne);
    entryList.Add(PasswordTwo);

    entryList.Add(ConfirmPasswordOne);
    entryList.Add(ConfirmPasswordTwo);
}

private void Entry_FieldChanged(object sender,TextChangedEventArgs e)
{
    var entry = sender as Entry;

    const string numregex = "^[0-9]+$";

    if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text,numregex) || entry.Text == ""))
    {
        if (entry.Text.Length >= 1)
        {
            var index = entryList.IndexOf(entry);
            var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
            var next = entryList.ElementAt(nextIndex);
            if (entry == ConfirmPasswordTwo)
            { }
            else
            {
                next?.Focus();
            }
        }
    }
    else
    {
        var index = entryList.IndexOf(entry);
        var prevIndex = (index) == 0 ? 0 : index - 1;
        var prev = entryList.ElementAt(prevIndex);
        if (entry == PasswordOne)
        { }
        else
        {
            prev?.Focus();
        }
    }
}

更新

CustomEntry.xml.cs

namespace Mobile.App
{
    public class CustomEntry : Entry
    {

        public delegate void BackspaceEventHandler(object sender,EventArgs e);

        public event BackspaceEventHandler OnBackspace;
        public CustomEntry() { }

        public void OnBackspacepressed()
        {
            OnBackspace?.Invoke(null,null);
        }

    }
}

CustomEntryRenderer

[assembly: ExportRenderer(typeof(CustomEntry),typeof(CustomEntryRenderer))]
namespace Mobile.App.Droid
{
    public class CustomEntryRenderer : EntryRenderer,IInputFilter
    {
        public CustomEntryRenderer(Context context) : base(context)
        {
        }

        public ICharSequence FilterFormatted(ICharSequence source,int start,int end,ISpanned dest,int dstart,int dend)
        {
            if (string.IsNullOrWhiteSpace(source.ToString()))
            {
                var entry = (CustomEntry)Element;
                entry.OnBackspacepressed();
            }
            return source;
        }

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

            if (Control == null)
            {
                return;
            }

            if (Control != null)
            {
                int length = Control.Text.Length;
                Control.SetSelection(length);
                SetColors();
            };

            Control.SetFilters(new IInputFilter[] { this });
        }

        private void SetColors()
        {
            Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
        }
    }
}

.xml.cs

public partial class PasswordPage : ContentPage
{

    public List<Entry> entryList { get; set; }

    public PasswordPage()
    {
        InitializeComponent();
        ValidatePassword();

        entryList = new List<Entry>();
        entryList.Add(PasswordOne);
        entryList.Add(PasswordTwo);

        entryList.Add(ConfirmPasswordOne);
        entryList.Add(ConfirmPasswordTwo);
    }

    Entry entry;
    private void Entry_FieldChanged(object sender,TextChangedEventArgs e)
    {
        entry = sender as Entry;

        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text,numregex) || entry.Text == ""))
        {
            if (entry.Text.Length >= 1)
            {
                var index = entryList.IndexOf(entry);
                var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
                var next = entryList.ElementAt(nextIndex);
                if (entry == ConfirmPasswordTwo)
                { }
                else
                {
                    next?.Focus();
                }
            }
        }
        //else
        //{
        //    var index = entryList.IndexOf(entry);
        //    var prevIndex = (index) == 0 ? 0 : index - 1;
        //    var prev = entryList.ElementAt(prevIndex);
        //    if (entry == PasswordOne)
        //    { }
        //    else
        //    {
        //        prev?.Focus();
        //    }
        //}
    }

    private void CustomEntry_OnBackspace(object sender,EventArgs e)
    {
        const string numregex = "^[0-9]+$";

        if (!(System.Text.RegularExpressions.Regex.IsMatch(entry.Text,numregex) || entry.Text == ""))
        {
            var last = entryList.ElementAt(entryList.Count - 1);
            last?.Focus();
        }
        else
        {
            return;
        }
    }
}

.xml

<local:CustomEntry
    x:Name="PasswordOne"
    IsPassword="True"
    Keyboard="Numeric"
    MaxLength="1"
    ReturnType="Next"
    TextChanged="Entry_FieldChanged"
    OnBackspace="CustomEntry_OnBackspace"
    Unfocused="Password_Unfocused" />

解决方法

如果您想在按下 Backspace 按钮时聚焦到最后一个条目,可以使用自定义渲染器。

自定义条目:

 public class CustomEntry : Entry
{
     
    public delegate void BackspaceEventHandler(object sender,EventArgs e);
   
    public event BackspaceEventHandler OnBackspace;
    public CustomEntry() { }

    public void OnBackspacePressed()
    {
        OnBackspace?.Invoke(null,null);
    }

}

自定义渲染器:

[assembly: ExportRenderer(typeof(CustomEntry),typeof(CustomEntryRenderer))]
namespace Test.Droid
{
public class CustomEntryRenderer : EntryRenderer,Android.Text.IInputFilter
{
    public CustomEntryRenderer(Context context) : base(context)
    {
    }

    public ICharSequence FilterFormatted(ICharSequence source,int start,int end,ISpanned dest,int dstart,int dend)
    {
        if (string.IsNullOrWhiteSpace(source.ToString()))
        {
            var entry = (CustomEntry)Element;
            entry.OnBackspacePressed();
        }
        return source;
    }

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

        if (Control == null)
        {
            return;
        }

        Control.SetFilters(new IInputFilter[] { this });

    }

    

}
}

XML:

  <local:CustomEntry
            x:Name="PasswordOne"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding PasswordOne}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="PasswordTwo"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding PasswordTwo}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="ConfirmPasswordOne"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding ConfirmPasswordOne}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="ConfirmPasswordTwo"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Done"
            Text="{Binding ConfirmPasswordTwo}"
            TextChanged="Entry_FieldChanged" />

背后的代码:

   public List<Entry> entryList { get; set; }
    public Page3()
    {
        InitializeComponent();
        entryList = new List<Entry>();
        entryList.Add(PasswordOne);
        entryList.Add(PasswordTwo);

        entryList.Add(ConfirmPasswordOne);
        entryList.Add(ConfirmPasswordTwo);
    }
    Entry entry;
    private void Entry_FieldChanged(object sender,TextChangedEventArgs e)
    {
        entry = sender as Entry;

        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text,numregex) || entry.Text == ""))
        {
            if (entry.Text.Length >= 1)
            {
                var index = entryList.IndexOf(entry);
                var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
                var next = entryList.ElementAt(nextIndex);
                if (entry == ConfirmPasswordTwo)
                { }
                else
                {
                    next?.Focus();
                }
            }
        }
        //else
        //{
        //    var index = entryList.IndexOf(entry);
        //    var prevIndex = (index) == 0 ? 0 : index - 1;
        //    var prev = entryList.ElementAt(prevIndex);
        //    if (entry == PasswordOne)
        //    { }
        //    else
        //    {
        //        prev?.Focus();
        //    }
        //}
    }

    private void CustomEntry_OnBackspace(object sender,EventArgs e)
    {
        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text,numregex) || entry.Text == ""))
        {
            var last = entryList.ElementAt(entryList.Count - 1);
            last?.Focus();
        }
        else
        {
            return;
        }
    }

请注意,我们需要注释 Entry_FieldChanged 的 else 部分。

更新:

在 .xml.cs 页面上,我不能使用 Entry 作为基类。它返回一个错误。目前,我正在使用 ContentPage

创建一个新类,不要直接使用 Entry 作为 ContentPage 的基类。

如果您仍然有错误,请提供错误信息。

对于 local:Entry,我添加了 xmlns:local="http://xamarin.com/schemas/2014/forms" 作为参考,对吗?

不,local 是 CustomEntry 的位置。对我来说,CustomEntry 位于名为 Test 的 Xamarin.forms 项目中。

 xmlns:local="clr-namespace:Test"

我的项目中已经有一个 Entry Custom Renderer(下面是代码)。 OnBackspacePressed();说 Entry 不包含 OnBackspacePressed() 的定义。

您是否先创建自定义条目?我在 CustomEntry 控件中定义了 OnBackspacePressed

无需点击/触摸/点击条目编号。 2,如果您从任何条目单击退格按钮,您只需返回上一个条目,无需在任何条目上手动点击/点击/触摸

 private void CustomEntry_OnBackspace(object sender,numregex) || entry.Text == ""))
        {
            //var last = entryList.ElementAt(entryList.Count - 1);
            //last?.Focus();
            var index = entryList.IndexOf(entry);
            var pre = entryList.ElementAt(index - 1);
            pre?.Focus();
        }
        else
        {
            return;
        }
    }