You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android自定义TextInputEditText问题:光标始终停留在起始位置

解决自定义密码输入框光标跳回起始位置的问题

嘿,这个问题我太熟了!自定义TextInputEditText做密码输入框时,要是直接在文本变化回调里替换内容,大概率会碰到光标每次都跳回开头的坑。先给你捋捋原因,再上解决方案:

为啥光标会乱跑?

核心原因是:当你调用setText()更新输入框内容时,Android系统默认会把光标重置到文本的起始位置。而且如果没做特殊处理,还会触发TextWatcher的无限循环回调,雪上加霜。

一步步解决问题

1. 加个标记位避免循环更新

首先得区分开「用户手动输入」和「代码内部更新文本」,不然每次setText()都会触发文本变化监听,陷入死循环。

2. 先存光标位置再恢复

在更新文本前,先把当前光标所在的位置记下来,等文本替换完成后,再把光标挪回原来的位置(如果位置超出新文本长度,就放末尾)。

修正后的完整代码

public class CustomInputEditTextWithSecret extends TextInputEditText {
    private final String SECRET_MASK = "●●●●●●"; // 换成你要显示的秘密字符串,比如你的"123456"
    private boolean isUpdating = false; // 标记是否是内部更新,防止循环
    private String realPassword = ""; // 存用户真实输入的密码,因为显示的是掩码

    public CustomInputEditTextWithSecret(Context context) {
        super(context);
        init();
    }

    public CustomInputEditTextWithSecret(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomInputEditTextWithSecret(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 保持密码输入的系统特性
        setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
        
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // 如果是内部更新文本,直接跳过,避免循环
                if (isUpdating) return;

                isUpdating = true;
                // 先保存当前光标位置
                int cursorPos = getSelectionStart();
                // 记录用户真实输入的内容(这里根据你的需求调整,比如追加新输入的字符)
                if (count > 0) {
                    realPassword += s.subSequence(start, start + count).toString();
                } else if (before > 0) {
                    // 处理删除操作
                    if (cursorPos < realPassword.length()) {
                        realPassword = realPassword.substring(0, cursorPos) + realPassword.substring(cursorPos + before);
                    } else {
                        realPassword = realPassword.substring(0, realPassword.length() - before);
                    }
                }
                // 设置显示的掩码文本
                setText(SECRET_MASK);
                // 恢复光标位置,不能超过掩码长度
                setSelection(Math.min(cursorPos, SECRET_MASK.length()));
                isUpdating = false;
            }

            @Override
            public void afterTextChanged(Editable s) {}
        });
    }

    // 对外提供获取真实密码的方法
    public String getRealPassword() {
        return realPassword;
    }
}

几个关键细节

  • isUpdating标记位:这是避免无限循环的关键,不然每次setText()都会触发onTextChanged,一直重复更新。
  • 真实密码单独存储:因为输入框显示的是掩码(比如你的"123456"),所以必须用单独变量存用户实际输入的内容,不然拿不到真实密码。
  • 光标位置处理:恢复光标时要判断是否超出掩码长度,不然会报错,直接设到末尾就好。

额外小建议

如果你的需求只是显示密码掩码,其实不用自定义View,直接用系统的TextInputEditText,设置inputType="textPassword",系统会自动显示圆点掩码,还不会有光标问题。只有当你需要自定义掩码内容(比如固定显示"123456")时,才需要上面的自定义方案。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:06:15