AutoCompleteTextView触发setSpan(-1…-1)索引越界错误的原因与解决方法
这个setSpan (-1 ... -1) starts before 0异常是Android系统底层的文本处理逻辑抛出,和你业务代码没有直接调用setSpan无关:系统的ArrowKeyMovementMethod在处理AutoCompleteTextView的触摸事件时,尝试给文本设置光标位置,当文本内容为空、或者焦点切换时序和系统光标定位逻辑冲突时,会算出非法的-1位置,触发越界崩溃。
AddNote页面触发而EditNote页面不触发的核心原因就是你提到的setOnFocusChangedListener差异:AddNote页面的AutoCompleteTextView默认是空内容,你在焦点变化回调里执行的操作(比如修改文本、弹出/收起软键盘、调整下拉列表)和系统的光标定位逻辑撞了执行时序,就会偶发这个异常;EditNote页面有初始文本,本身光标位置计算不会出现-1的非法值,所以不会触发。
方案1:调整焦点监听的执行时序(最简单,优先尝试)
把你在setOnFocusChangedListener里写的所有逻辑,放到View的post队列里执行,等系统处理完当前的触摸、光标定位逻辑后再运行你的业务代码,避免时序冲突:
yourAutoCompleteTextView.setOnFocusChangeListener((v, hasFocus) -> { // 把原有逻辑包在post中 v.post(() -> { // 原有逻辑写在这:比如弹出下拉、操作软键盘、修改文本等 }); });
同时注意不要给AutoCompleteTextView设置setText(null),空内容统一用setText(""),避免系统处理null文本时算出非法光标位置。
方案2:自定义安全组件兜底(完全杜绝崩溃)
如果调整时序后还是偶发,可以自定义封装AutoCompleteTextView,直接捕获系统抛出的越界异常,不影响业务逻辑:
import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import androidx.appcompat.widget.AppCompatAutoCompleteTextView; public class SafeAutoCompleteTextView extends AppCompatAutoCompleteTextView { public SafeAutoCompleteTextView(Context context) { super(context); } public SafeAutoCompleteTextView(Context context, AttributeSet attrs) { super(context, attrs); } public SafeAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { try { return super.onTouchEvent(event); } catch (IndexOutOfBoundsException ignore) { // 捕获系统内部的越界异常,不会崩溃 return false; } } }
之后把布局文件中原来的AutoCompleteTextView替换为这个自定义组件即可,原有初始化、适配器逻辑完全不需要修改。
额外检查项
- 确认你的适配器返回的字符串列表中没有null值,所有空内容统一用空字符串
""代替 - 如果有动态修改适配器数据集的操作,修改后必须调用
notifyDataSetChanged(),且不要在触摸/焦点回调中同步修改数据集
内容的提问来源于stack exchange,提问作者svstackoverflow

