需求背景:
實現搜索框的搜索聯想功能,當輸入框輸入字符時候,立刻進行網絡請求,將相關推薦展示在下方的列表中,要求每次展示的一定是當前最新輸入的內容的推薦詞。
實現思路:
展示搜索詞聯想需要滿足如下三點:
1.搜索框輸入內容發生變化需要立刻進行網絡請求搜索關聯詞彙
2.搜索詞輸入內容變化較快,需要保證每次展示出來的聯想詞都是輸入框中最新的輸入內容所對應的聯想詞
3.每當有新的請求時候需要及時取消掉現在正在進行的網絡請求,以免網絡資源浪費。
綜合以上三點,發現網上上一些現有方案都是基於RXjava實現,在一個外文培訓網站發現一個較爲完善的方案,通過debounce、filter、distinctUntilChanged、switchMap幾個方法來滿足以上三點功能,這樣原本複雜的邏輯處理就通過簡單的幾個鏈式調用完成了,修改補充後的代碼如下:
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (!TextUtils.isEmpty(editText.getText().toString())) {
if(publishSubject!=null) {
publishSubject.onNext(editText.getText().toString());
}
}
}
});
private void initPublishSubject() {
compositeDisposable = new CompositeDisposable();
publishSubject = PublishSubject.create();
Disposable disposable = publishSubject.debounce(300, TimeUnit.MILLISECONDS)
.filter(new Predicate<String>() {
@Override
public boolean test(String s) throws Exception {
if(TextUtils.isEmpty(s)){
return false;
} else {
return true;
}
}
})
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<ArrayList<String>>>() {
@Override
public ObservableSource<ArrayList<String>> apply(String s) throws Exception {
return getSearchObservable(s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(Object o) throws Exception {
ArrayList<String> list = (ArrayList<String>)o;
if(list!=null && list.size()!=0) {
//將返回的聯想詞展示到列表中
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
MyLog.d(TAG,"fail:" + throwable.getMessage());
}
});
compositeDisposable.add(disposable);
}
private Observable<ArrayList<String>> getSearchObservable(final String query) {
return Observable.create(new ObservableOnSubscribe<ArrayList<String>>() {
@Override
public void subscribe(ObservableEmitter<ArrayList<String>> observableEmitter) throws Exception {
ArrayList<String> list = new ArrayList<>();
try {
//通過網絡獲取聯想詞,將其賦值給list列表
} catch (Exception e) {
if (!observableEmitter.isDisposed()) {
observableEmitter.onError(e);
}
}
observableEmitter.onNext(list);
}
}).subscribeOn(Schedulers.io());
}
得到搜索聯想詞之後就是將其填充到recycleview中,這一步較爲簡單這裏不再詳述,主要對以上代碼中使用的主要RXjava操作符簡單解析如下:
1.Debounce
僅在過了一段指定的時間後還沒有發射數據時才發射一個數據主要用於過濾掉髮射速率太快的數據,比如以上代碼中,如果首次輸入了a,然後在指定的300毫秒時間間隔內,有輸入了b,此時就會過濾掉數據a,重新以ab爲開始,重新等待300毫秒,如果這300毫秒內沒有新的輸入,就發射數據ab,如果有新的輸入比如c,就過濾掉數據ab,以數據abc爲新的數據繼續等待300毫秒,如果300毫秒時間到了,沒有新的輸入,就發射數據abc,這樣的好處是過濾掉了一些中間不必要的數據。
2.Filter:
使用一個指定的函數測試數據,只有通過測試的數據才能夠發射,比如以上代碼中,只有非空的數據才能夠發射,空數據則過濾掉
3.DistinctUntilChanged:
DistinctUntilChanged與distinct操作符類似,功能都是避免重複,只允許通過未發射過的數據,假設最後一個請求的搜索詞是abc,然後用戶又馬上刪除了C,然後又再次輸入一個c這樣搜索詞依然是abc,這樣最後的這個abc的請求就會過濾掉。
4.SwitchMap:
switchMap運算符用於避免多餘的網絡呼叫結果,該結果對於顯示給用戶而言並不需要更多,只需要最新一次的結果即可。假設最後輸入的是ab,如果正在進行ab的聯想詞請求時候用戶又繼續輸入了c,此時用戶只對abc的聯想詞感興趣,而不再需要ab的聯想詞,switchMap正好可以解決這個問題,它僅返回最新的搜索結果而忽略掉之前在進行的請求。