java.lang.IndexOutOfBoundsException: setSpan ( ... ) ends beyond length 剖析

背景

闲来无事,敲代码,忽然一个Exception引起了我的注意。
在这里插入图片描述

我们在设置中打开拼写检查工具功能的时候,SuggesttionsPopupWindow这个系统弹窗,就会在我们编辑EditText的时候,自动弹出,提示我们补全。
在这里插入图片描述在这里插入图片描述

分析

看trackstack的信息,应该是点击其中一项候选词的时候发生的奔溃。让我们先看看SuggesttionsPopupWindow在处理点击事件的时候做了什么。

@Override
 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
     SuggestionInfo suggestionInfo = mSuggestionInfos[position];
     replaceWithSuggestion(suggestionInfo);
     hideWithCleanUp();
}

代码很简洁,我们继续看看replaceWithSuggestion这个方法,代码挺长的,只保留重点

private void replaceWithSuggestion(@NonNull final SuggestionInfo suggestionInfo) {
    ···
    //suggestion是点击选中的候选词
	final String suggestion = suggestionInfo.mText.subSequence(
		suggestionStart, suggestionEnd).toString();
	//替换
	mTextView.replaceText_internal(spanStart, spanEnd, suggestion);

	//把被替换的词放到候选词列表
	String[] suggestions = targetSuggestionSpan.getSuggestions();
	suggestions[suggestionInfo.mSuggestionIndex] = originalText;

	// Restore previous SuggestionSpans
	//候选词和被替换词的长度差值
	final int lengthDelta = suggestion.length() - (spanEnd - spanStart);
	for (int i = 0; i < length; i++) {
	// Only spans that include the modified region make sense after replacement
	// Spans partially included in the replaced region are removed, there is no
	// way to assign them a valid range after replacement
		if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) {
			//这里在计算setSpan_internal的时候end加上了候选词和被替换词的长度差值,其实就是默认
			//候选词替换后可以完全显示,假如我们设置了长度限制,例如maxlength,就会引发上述的奔溃
			mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
			suggestionSpansEnds[i] + lengthDelta, suggestionSpansFlags[i]);
		}
	}
	// Move cursor at the end of the replaced word
	final int newCursorPosition = spanEnd + lengthDelta;
	//这里也有同样的问题
	mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}

解决方案

因此,这个问题的根本原因是,replaceWithSuggestion这个方法,并没有考虑EditText限制长度的情况。既然如此,我们只好做一下边界保护。

public class SuggestionEditText extends EditText {
    public SuggestionEditText(Context context) {
        super(context);
    }

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

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

    /**
     * Sets a span on the specified range of text
     */
    protected void setSpan_internal(Object span, int start, int end, int flags) {
        getText().setSpan(span, start, Math.min(end, getText().length()), flags);
    }

    /**
     * Moves the cursor to the specified offset position in text
     */
    protected void setCursorPosition_internal(int start, int end) {
        Selection.setSelection(getText(), Math.min(start, getText().length()), 
        	Math.min(end, getText().length()));
    }

}

彩蛋

可能有人会说,这2个方法可是标记为hide,是不是不大稳当。那么还有保底一个方法就是设置InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS禁用该功能。
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值