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




