You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android自定义Entry Renderer中BeforeTextChanged无法获取待输入文本

解决Android自定义Entry Renderer中获取即将输入文本并控制输入的问题

嘿,我明白你现在的困扰——你尝试了两种方式监听BeforeTextChanged,但都拿不到即将输入的文本,其实核心问题是对这个事件的触发时机和参数含义理解有点偏差,我来给你理清楚并提供可行的方案:

首先得明确:BeforeTextChanged触发时,即将输入的文本还根本没被写入到EditText里。不管是你实现ITextWatcher的方法,还是直接订阅控件的BeforeTextChanged事件,拿到的都是修改前的原文本,新输入的内容此时还没被加载到控件中,所以你自然拿不到。

下面给你两种符合需求的解决方案,都不需要用Behavior:

方案一:用OnTextChanged捕获新内容并做校验回滚

OnTextChanged是在文本正在修改时触发的,此时已经能拿到包含新输入内容的完整文本。你可以在这里对比修改前后的内容,判断是否允许这次输入,如果不符合要求就回滚到修改前的状态:

// 在自定义Renderer的OnElementChanged方法里添加TextWatcher
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
    base.OnElementChanged(e);
    if (Control != null)
    {
        Control.AddTextChangedListener(new CustomTextWatcher(this));
    }
}

// 自定义TextWatcher类
private class CustomTextWatcher : Java.Lang.Object, ITextWatcher
{
    private readonly CustomEntryRenderer _renderer;
    private string _lastValidText;

    public CustomTextWatcher(CustomEntryRenderer renderer)
    {
        _renderer = renderer;
        _lastValidText = renderer.Control?.Text ?? string.Empty;
    }

    public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
    {
        // 记录修改前的合法文本,用来后续回滚
        _lastValidText = s?.ToString() ?? string.Empty;
    }

    public void OnTextChanged(ICharSequence s, int start, int before, int count)
    {
        string currentText = s?.ToString() ?? string.Empty;
        // 这里currentText就是包含新输入内容的完整文本
        bool isAllowed = ValidateInput(_lastValidText, currentText, start, count);

        if (!isAllowed)
        {
            // 不允许的话,回滚到上一次的合法文本
            _renderer.Control.Text = _lastValidText;
            // 恢复光标位置,避免用户体验变差
            _renderer.Control.SetSelection(start);
        }
    }

    public void AfterTextChanged(IEditable s)
    {
        // 可选:文本修改完成后的额外操作
    }

    // 这里写你的自定义校验逻辑
    private bool ValidateInput(string oldText, string newText, int changeStart, int addedLength)
    {
        // 示例:只允许输入数字
        string addedContent = newText.Substring(changeStart, addedLength);
        return addedContent.All(char.IsDigit);
    }
}

方案二:用InputFilter拦截输入(更推荐)

如果你想在文本被插入之前就判断是否允许,Android原生的InputFilter是更合适的工具,它能直接拦截即将输入的内容,效率更高,也更贴合"仅特定场景允许插入"的需求:

protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
    base.OnElementChanged(e);
    if (Control != null)
    {
        // 添加自定义InputFilter到EditText
        Control.SetFilters(new IInputFilter[] { new CustomInputFilter() });
    }
}

// 自定义InputFilter类
private class CustomInputFilter : Java.Lang.Object, IInputFilter
{
    public ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
    {
        // source就是即将输入的文本(如果是粘贴,source就是完整的粘贴内容)
        // start/end:source中要插入的部分的起止位置
        // dest:当前EditText里的原有文本
        // dstart/dend:原有文本中要被替换的部分的起止位置

        string inputToCheck = source.ToString().Substring(start, end - start);
        
        // 校验输入是否符合要求
        if (!IsInputAllowed(inputToCheck))
        {
            // 返回空字符串,直接拒绝这次输入
            return new Java.Lang.String("");
        }

        // 返回null表示允许输入(也可以返回修改后的文本,比如把大写转小写)
        return null;
    }

    // 自定义你的输入规则
    private bool IsInputAllowed(string input)
    {
        // 示例:只允许输入字母和数字,禁止特殊字符
        return input.All(c => char.IsLetterOrDigit(c));
    }
}

再回头说下你原有代码的问题

不管是实现ITextWatcher.BeforeTextChanged还是订阅Control.BeforeTextChanged,这两个时机都是在文本修改发生前,新内容还没有被写入控件,所以你拿到的s或者args.Text都是修改前的原文本,Control.Text也还是旧内容,自然获取不到即将输入的文本啦。

内容的提问来源于stack exchange,提问作者VahidShir

火山引擎 最新活动