我最近遇到了完全相同的问题,我编写了自己的自定义算法来检测 TextWatcher 输出中的差异。
算法 -
我们存储 4 样东西——
- 旧选择大小
- 旧文本
- 光标/选择之前的旧文本序列。
- 光标/选择后的旧文本序列。
以上 4 件事在beforeTextChanged()
回调期间更新。
现在在onTextChanged()
回调期间,我们计算以下两件事 -
- 光标/选择之前的新文本序列。
- 光标/选择后的新文本序列。
现在可能出现以下情况 -
情况1
New text sequence before the cursor/selection == Old text sequence before the cursor/selection
和New text sequence after the cursor/selection isASuffixOf Old text sequence after the cursor/selection
这是一个删除转发案例。删除的字符数可以通过 oldText 长度减去 newText 长度来计算。
例子 -
旧文本 = Hello wo|rld(|
代表光标)
光标/选择之前的旧文本序列 =Hello wo
光标/选择后的旧文本序列 =rld
旧选择大小 =0
新文本 = Hello wo|ld(|
代表光标)
光标/选择之前的新文本序列 =Hello wo
光标/选择之后的新文本序列 =ld
显然,这是向前删除 1 个字符的情况。
案例2
New text sequence after the cursor/selection == Old text sequence after the cursor/selection
和New text sequence before the cursor/selection isAPrefixOf Old text sequence before the cursor/selection
这是一个向后删除的情况。删除的字符数可以通过 oldText 长度减去 newText 长度来计算。
例子 -
旧文本 = Hello wo|rld(|
代表光标)
光标/选择之前的旧文本序列 =Hello wo
光标/选择后的旧文本序列 =rld
旧选择大小 =0
新文本 = Hello w|rld(|
代表光标)
光标/选择之前的新文本序列 =Hello w
光标/选择之后的新文本序列 =rld
显然,这是向后删除 1 个字符的情况。
案例3
New text sequence after the cursor/selection == Old text sequence after the cursor/selection
和Old text sequence before the cursor/selection isAPrefixOf New text sequence before the cursor/selection
这是一个插入盒。old text sequence from cursor + old text sequence after cursor
可以通过从新文本字符串中删除 来计算确切的插入字符串。
例子 -
旧文本 = Hello wo|rld(|
代表光标)
光标/选择之前的旧文本序列 =Hello wo
光标/选择后的旧文本序列 =rld
旧选择大小 =0
新文本 = Hello wo123|rld(|
代表光标)
光标/选择之前的新文本序列 =Hello wo123
光标/选择之后的新文本序列 =rld
显然,这是插入的情况,插入的字符串是123
。
案例4
如果上述情况都不满足,那么我们可以说它是一个替换情况。并且替换数据已经由onTextChanged
回调中的 TextWatcher 提供。
这是上述算法的代码 -
class MyTextWatcher : android.text.TextWatcher {
var oldSelectionSize = 0
var oldText: String = ""
var oldSequenceBeforeCursor: String = ""
var oldSequenceAfterCursor: String = ""
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
oldSelectionSize = editText.selectionEnd - editText.selectionStart
oldText = s.toString()
oldSequenceBeforeCursor = s?.subSequence(0, editText.selectionStart).toString()
oldSequenceAfterCursor = s?.subSequence(editText.selectionEnd, s.length).toString()
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
s?.toString()?.let { newText ->
val newSequenceBeforeCursor = newText.subSequence(0, selectionStart).toString()
val newSequenceAfterCursor = newText.subSequence(selectionEnd, newText.length)
.toString()
if (newSequenceBeforeCursor == oldSequenceBeforeCursor &&
oldSequenceAfterCursor.endsWith(newSequenceAfterCursor))
// handle delete forward
// number of characters to delete ==>
// if(oldSelectionSize > 0) then deleted number of characters = oldSelectionSize
// else number of characters to delete = oldText.length - newText.length
else if (newSequenceAfterCursor == oldSequenceAfterCursor &&
oldSequenceBeforeCursor.startsWith(newSequenceBeforeCursor))
// handle delete backward
// number of characters to delete ==>
// if(oldSelectionSize > 0) then deleted number of characters = oldSelectionSize
// else number of characters to delete = oldText.length - newText.length
else if (newSequenceAfterCursor == oldSequenceAfterCursor &&
newSequenceBeforeCursor.startsWith(oldSequenceBeforeCursor))
// handle insert
// inserted string = (newText - oldSequenceBeforeCursor) - oldSequenceAfterCursor
else
// handle replace
// replace info already provided in `onTextChanged()` arguments.
}
}
override fun afterTextChanged(s: Editable?) {
}
}