私は最近まったく同じ問題に直面し、TextWatcher の出力から差分を検出する独自のカスタム アルゴリズムを作成しました。
アルゴリズム -
私たちは4つのものを保管します -
- 古い選択サイズ
- 古いテキスト
- カーソル/選択の前の古いテキスト シーケンス。
- カーソル/選択の後の古いテキスト シーケンス。
beforeTextChanged()
上記の 4 つのことは、コールバック中に更新されます。
コールバック中に、onTextChanged()
次の 2 つのことを計算します -
- カーソル/選択の前の新しいテキスト シーケンス。
- カーソル/選択の後の新しいテキスト シーケンス。
現在、次のケースが考えられます -
ケース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
上記のケースのいずれも満たされない場合、それは交換ケースであると言えます。また、置き換えデータは、コールバックで TextWatcher によって既に提供されていonTextChanged
ます。
上記のアルゴリズムのコードは次のとおりです-
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?) {
}
}