onTextChanged
EditTextは一般的に使用されるテキスト入力コントロールですが、そのコールバックインターフェイスは使いやすいように設計されていません。3つのインターフェイスを実装する必要があります。私はほとんどのシナリオのみを気にします。onTextChanged
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.d(TAG, "query: ${
s}")
}
})
そして、最も懸念されるのonTextChanged
は、そのセマンティクスが単純すぎるため(変更するたびにコールバックする)、一般的な实时搜索
シナリオなど、一部のビジネスシナリオでは期待される結果を達成できない場合、入力ボックスにキーワードを入力してリアルタイムで実行できることを望んでいます。この時点でLenovoまたはSearchを使用EditText#onTextChanged
すると、多くの問題が発生します。
複数のコールバック
たとえば、上記のコードでは、1つずつ入力a b c d e
して、次のログを取得します
query: a
query: ab
query: ab
query: abc
query: abcd
query: abcd
その中で、abとabcdは2回出現します。調査の結果、EditText
androidの設定に関連しています-TextWatcherイベントが複数回
発生しています-スタックオーバーフロー。もちろん、この問題はリアルタイム検索シーンでは発生しません。また、このコントロールがどのようにピットインしているかを反映しています。
頻繁にトリガーされる
入力フィードバック实时
効果があることが望ましいが、鋭すぎないようにしたい(高いメンテナンス|||人間の真)、たとえば、「A」「AA」「AAA」「AAAA」,说明我们的目的性很强,只对最后的
「AAAA」などの高速入力の組み合わせの場合結果を得るだけ
非同期の結果が正しくない
非同期呼び出しは入力後に“a” “aa”
開始されるため、複数の連続する非同期要求の結果のコールバックタイミングが期待を満たさない可能性があります。たとえば、入力は2つの非同期要求をトリガーしますが、「a」の結果が返され、入力ボックスがすでに「aa」状態のまま。
RxJava最適化を使用する
上記の問題はすべてEditText#onTextChanged
、予想されるビジネスシナリオを満たせないコールバックセマンティクスが原因で発生します。現時点では、RxJavaを使用してそれらを最適化できます。
複数回のコールバック
val queryPublisher = PublishSubject.create<String>()
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
queryPublisher.onNext(s.toString())
}
})
queryPublisher.distinctUntilChanged().subscribe({
Log.d(TAG, "query: ${
it}")
})
PublishSubject
ストリームパイプラインを提供しonTextChanged
ます。受信した元のコールバックを
distinctUntilChanged
使用して、各値が1回だけコールバックされるようにします。
query: a
query: ab
query: abc
query: abcd
頻繁なトリガー
queryPublisher.distinctUntilChanged()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
Log.d(TAG, "query: ${
it}")
})
debounce
防振を追加するために使用します。これthrottleLast
も同様の関数演算子ですが、効果はこの要件の期待を満たしていないことに注意してください。2つの違いを比較してみましょう。
-
デバウンス
が一定期間データを送信した後、新しいデータがない場合は実際にデータが送信されます。この期間中に新しいデータが送信された場合は、このデータが送信されるデータ項目として使用され、タイミングが再開されます。
-
スロットル
ラストは、各期間の最後のデータを送信します。ユーザーは、継続的な入力の途中で結果を要求する場合がありますが、これは期待に応えません。
非同期の結果が正しくない
2つの解決策があります。
処分する
private var searchDisposable: Disposable? = null
override fun onCreate(savedInstanceState: Bundle?) {
// 略...
queryPublisher.distinctUntilChanged()
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
Log.d(TAG, "query: ${
it}")
searchDisposable?.dispose()
searchDisposable = search(query = it)
.subscribe({
// 搜索结果显示
})
})
}
/**
* 结果请求
*/
fun search(query: String?): Observable<ArrayList<String>> {
// 略...
return Observable.empty<ArrayList<String>>()
}
新しい入力を取得したら、前の非同期リクエストを手動で停止します
switchMap
switchMap
オペレーターはに比べてdispose
よりエレガントです
RxJavaはswitchMap演算子も実装しています。これはflatMapとほぼ同じように動作しますが、ソースObservableによって新しいアイテムが発行されるたびに、以前に発行されたアイテムから生成されたObservableのサブスクライブを解除してミラーリングを停止し、現在のアイテムのミラーリングのみを開始します。
switchMap
そしてflatMap
同様に、また、ストリームを切り替えるために使用することができますが、新しいswitchMapストリームへのスイッチは、古いストリームは自動的に停止したとき。flatMapとの比較を通じて、その意味を理解してください。
-
flatMap
-
switchMap
上記の例では、switchMapの変換後、コードは非常に断固としたものになります。
override fun onCreate(savedInstanceState: Bundle?) {
// 略...
queryPublisher.distinctUntilChanged()
.debounce(500, TimeUnit.MILLISECONDS)
.switchMap {
search(it) }
.subscribe({
// 搜索结果显示
Log.d(TAG, "result: ${
it}")
})
}