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

Android自定义EditText实现括号补全触发IndexOutOfBoundsException求助

问题根因分析
  1. 时序冲突:afterTextChanged 回调执行时,EditText的文本测量渲染流程还未完成,你在回调内直接删除字符、修改光标位置,会和正在执行的渲染流程产生状态不同步。刚启动时初始文本渲染处于冷启动状态,这个冲突会直接触发越界异常;而移动过光标后渲染状态已经完成过一轮更新,冲突不会触发。
  2. 边界判断缺失:你在取 editable.subSequence(start, start + count) 前只判断了 editable.length() > start,没有判断 start + count <= editable.length(),初始位置插入时count值为3(插入的「[☢]」共3个字符),极端场景下会出现下标越界。
  3. 变量状态不匹配:onTextChanged 里存储的start和count可能和afterTextChanged里的editable实际状态不一致,首次冷启动输入时这个问题更容易触发。
修复方案

推荐直接替换原有占位符实现逻辑,代码更简洁也不会产生渲染冲突,修改后的MyEditText代码如下:

public class MyEditText extends AppCompatEditText {
    private final TextWatcher bracketWatcher = new TextWatcher() {
        // 标记位避免插入右括号时触发递归回调
        private boolean isAppendingBracket = false;

        @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 (isAppendingBracket || count != 1) return;
            // 检测到输入左括号时直接补全右括号
            if (s.charAt(start) == '[') {
                isAppendingBracket = true;
                getText().insert(start + 1, "]");
                // 光标定位到两个括号中间
                setSelection(start + 1);
                isAppendingBracket = false;
            }
        }

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

    private void init() {
        addTextChangedListener(bracketWatcher);
    }

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

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

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

如果你希望保留原有占位符的实现逻辑,仅需要给afterTextChanged的操作加边界校验,并把修改操作post到主线程消息队列尾部,等渲染流程完成后再执行即可,修改后的afterTextChanged代码如下:

@Override
public void afterTextChanged(Editable editable) {
    // 增加完整的边界校验
    if (count > 1 && start >=0 && (start + count) <= editable.length()) {
        CharSequence newText = editable.subSequence(start, start + count);
        int i = newText.toString().indexOf(CURSOR);
        if (i > -1) {
            // 延迟到当前渲染任务完成后再执行修改
            post(() -> {
                editable.delete(start + i, start + i + 1);
                setSelection(start + 1);
            });
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 16:30:02