最近在开发一个 Android 设备信息采集应用时,测试人员反馈了一个非常诡异的问题:在设备信息录入页面的一个文本输入框中,点击删除键非但无法删除字符,输入框内的文本反而越删越多,甚至出现重复拼接的现象。更让人头疼的是,这个问题只在英文语言环境下出现,且只在某个特定品牌的定制输入法上复现,换成系统默认输入法后一切正常。
经过排查与尝试,最终找到了根本原因并成功修复。
问题现象
-
用户在输入框中输入一段字符串(例如
SN2024001)。 -
点击删除键(Backspace),光标位置似乎正常,但文本长度非但没有减少,反而突然增长,甚至出现字符串重复。
-
继续点击删除键,文本持续异常增长,根本无法正常删除。
-
切换至系统默认输入法(如 Android 键盘 AOSP)后,删除功能恢复正常。
-
问题仅在英文语言环境下出现,中文环境正常;仅在某品牌定制输入法上复现,其他第三方输入法正常。
第一反应是怀疑自己的 TextWatcher 或 RecyclerView 适配器存在 bug。反复检查代码:
-
TextWatcher.afterTextChanged()中虽然更新了数据模型,但没有对文本进行任何二次修改。 -
适配器中存在
notifyDataSetChanged()调用,可能导致列表刷新时重新绑定了EditText内容。但即使改为notifyItemChanged()局部刷新,问题依旧。 -
尝试添加编辑保护标志、焦点判断等方式,均无效。
让测试人员切换到系统默认输入法(AOSP),问题消失;
核心思路
-
强制禁用输入法联想 :通过
setInputType增加TYPE_TEXT_FLAG_NO_SUGGESTIONS和TYPE_TEXT_VARIATION_VISIBLE_PASSWORD(密码类型会强制禁用联想,但字符仍正常显示)。 -
拦截删除键事件 :为
EditText设置OnKeyListener,当检测到KEYCODE_DEL时,手动删除最后一个字符,并返回true表示事件已消费。 -
清理可能干扰的配置 :清空
setPrivateImeOptions,防止旧配置影响。
代码实现
在列表适配器的 onBindViewHolder 中
java
// 强制输入类型,禁用联想
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
// 增加密码类型标记(仅影响输入法行为,界面仍是明文)
editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
// 清空私有参数
editText.setPrivateImeOptions("");
// 应用层接管删除键
editText.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
Editable text = ((EditText) v).getText();
if (text != null && text.length() > 0) {
// 删除最后一个字符
text.delete(text.length() - 1, text.length());
}
return true; // 事件已处理,不再交给输入法
}
return false;
});
同时在布局文件中editText添加下面的设置:
XML
android:inputType="textNoSuggestions"
android:importantForAutofill="no"
android:autofillHints=""
测试ok.