Android仿淘寶搜索聯(lián)想功能的示例代碼
現(xiàn)在不少應用都提供了搜索功能,有些還提供了搜索聯(lián)想。對于一個搜索聯(lián)想功能,最基本的實現(xiàn)流程為:客戶端通過監(jiān)聽輸入框內容的變化,當輸入框發(fā)生變化之后就會回調afterTextChanged方法,客戶端利用當前輸入框內的文字向服務器發(fā)起請求,服務器返回與該搜索文字關聯(lián)的結果給客戶端進行展示。服務器那邊,一般要做內存緩存池,就是把有可能的結果都放在內存中。
效果圖
APP這邊也有幾個重要的問題需要我們思考
- 當搜索詞為空時,不應該發(fā)起網(wǎng)絡請求。
- 在用戶連續(xù)輸入的情況下,可能會發(fā)起某些不必要的請求。例如用戶輸入了abc,那么按照上面的實現(xiàn),客戶端就會發(fā)起a、ab、abc三個請求。
- 如果用戶依次輸入了ab和abc,那么首先會發(fā)起關鍵詞為ab請求,之后再發(fā)起abc的請求,但是abc的請求如果先于ab的請求返回,那么就會造成用戶期望搜索的結果為abc,但是我們最終希望得到的結果卻是和ab關聯(lián)的。
我的方案是采用retrofit2+rxjava2來實現(xiàn)的,針對這幾個問題的大致思路如下,關于這幾個操作符的解釋,在Demo中有較完整的解釋
- 使用debounce操作符,當輸入框發(fā)生變化時,不會立刻將事件發(fā)布出去,而是等待200ms,如果在這段事件內,輸入框沒有發(fā)生變化,那么才發(fā)送該事件;反之,則在收到新的關鍵詞后,繼續(xù)等待200ms。
- 使用filter操作符,只有關鍵詞的長度大于0時才把事件發(fā)布出去。filter作用:對源Observable產生的結果按照指定條件進行過濾,只有滿足條件的結果才會提交給訂閱者
- 使用switchMap操作符,這樣當發(fā)起了abc的請求之后,即使ab的結果返回了,也不會發(fā)送給下游,從而避免了出現(xiàn)前面介紹的搜索詞和聯(lián)想結果不匹配的問題。switchMap操作符會保存最新的Observable產生的結果而舍棄舊的結果。
下面貼出關鍵代碼
private void initEdt() { editText = (EditText) findViewById(R.id.edt); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (s.toString().trim().isEmpty()) { mPop.dismiss(); } else { //輸入內容非空的時候才開始搜索 startSearch(s.toString()); } } }); mPublishSubject = PublishSubject.create(); mPublishSubject.debounce(200, TimeUnit.MILLISECONDS) //這里我們限制只有在輸入字符200毫秒后沒有字符沒有改變時才去請求網(wǎng)絡,節(jié)省了資源 .filter(new Predicate<String>() { //對源Observable產生的結果按照指定條件進行過濾,只有滿足條件的結果才會提交給訂閱者 @Override public boolean test(String s) throws Exception { //當搜索詞為空時,不發(fā)起請求 return s.length() > 0; } }) /** * flatmap:把Observable產生的結果轉換成多個Observable,然后把這多個Observable “扁平化”成一個Observable,并依次提交產生的結果給訂閱者 *concatMap:操作符flatMap操作符不同的是,concatMap操作符在處理產生的Observable時, 采用的是“連接(concat)”的方式,而不是“合并(merge)”的方式, 這就能保證產生結果的順序性,也就是說提交給訂閱者的結果是按照順序提交的,不會存在交叉的情況 *switchMap:與flatMap操作符不同的是,switchMap操作符會保存最新的Observable產生的 結果而舍棄舊的結果 **/ .switchMap(new Function<String, ObservableSource<String>>() { @Override public ObservableSource<String> apply(String query) throws Exception { return getSearchObservable(query); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DisposableObserver<String>() { @Override public void onNext(String s) { //顯示搜索聯(lián)想的結果 showSearchResult(s); } @Override public void onError(Throwable throwable) { } @Override public void onComplete() { } }); mCompositeDisposable = new CompositeDisposable(); mCompositeDisposable.add(mCompositeDisposable); } //開始搜索 private void startSearch(String query) { mPublishSubject.onNext(query); } private Observable<String> getSearchObservable(final String query) { return Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception { Log.d(TAG, "開始請求,關鍵詞為:" + query); try { Thread.sleep(100); //模擬網(wǎng)絡請求,耗時100毫秒 } catch (InterruptedException e) { if (!observableEmitter.isDisposed()) { observableEmitter.onError(e); } } if (!(query.contains("科") || query.contains("耐") || query.contains("七"))) { //沒有聯(lián)想結果,則關閉pop mPop.dismiss(); return; } Log.d("SearchActivity", "結束請求,關鍵詞為:" + query); observableEmitter.onNext(query); observableEmitter.onComplete(); } }).subscribeOn(Schedulers.io()); }
下面是針對幾個操作符,從官網(wǎng)download下來的東西,供大家一起學習
debounce
debounce原理類似于我們在收到請求之后,發(fā)送一個延時消息給下游,如果在這段延時時間內沒有收到新的請求,那么下游就會收到該消息;而如果在這段延時時間內收到來新的請求,那么就會取消之前的消息,并重新發(fā)送一個新的延時消息,以此類推。
而如果在這段時間內,上游發(fā)送了onComplete消息,那么即使沒有到達需要等待的時間,下游也會立刻收到該消息。
filter
filter的原理很簡單,就是傳入一個Predicate函數(shù),其參數(shù)為上游發(fā)送的事件,只有該函數(shù)返回true時,才會將事件發(fā)送給下游,否則就丟棄該事件。
switchMap
switchMap的原理是將上游的事件轉換成一個或多個新的Observable,但是有一點很重要,就是如果在該節(jié)點收到一個新的事件之后,那么如果之前收到的時間所產生的Observable還沒有發(fā)送事件給下游,那么下游就再也不會收到它發(fā)送的事件了。
如上圖所示,該節(jié)點先后收到了紅、綠、藍三個事件,并將它們映射成為紅一、紅二、綠一、綠二、藍一、藍二,但是當藍一發(fā)送完事件時,綠二依舊沒有發(fā)送事件,而最初綠色事件在藍色事件之前,那么綠二就不會發(fā)送給下游。
- flatmap:把Observable產生的結果轉換成多個Observable,然后把這多個Observable“扁平化”成一個Observable,并依次提交產生的結果給訂者
- concatMap:flatMap操作符不同的是,concatMap操作符在處理產生的Observable時,采用的是“連接(concat)”的方式,而不是“合并(merge)”的方式,這就能保證產生結果的順序性,也就是說提交給訂閱者的結果是按照順序提交的,不會存在交叉的情況
- switchMap:與flatMap操作符不同的是,switchMap操作符會保存最新的Observable產生的結果而舍棄舊的結果
GitHub地址(完整Demo,歡迎下載)
https://github.com/zhouxu88/SearchDemo
rxjava2學習地址
https://github.com/ReactiveX/RxJava
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android畫板開發(fā)之添加背景和保存畫板內容為圖片
這篇文章主要為大家詳細介紹了Android畫板開發(fā)之添加背景和保存畫板內容為圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12Android 中自定義ContentProvider與ContentObserver的使用簡單實例
這篇文章主要介紹了Android 中自定義ContentProvider與ContentObserver的使用簡單實例的相關資料,這里提供實例幫助大家學習理解這部分內容,需要的朋友可以參考下2017-09-09Android編程基于自定義控件實現(xiàn)時鐘功能的方法
這篇文章主要介紹了Android編程基于自定義控件實現(xiàn)時鐘功能的方法,結合實例形式詳細分析了Android自定義控件的定義及時鐘功能相關實現(xiàn)技巧,需要的朋友可以參考下2018-03-03Android 自定義view模板并實現(xiàn)點擊事件的回調
這篇文章主要介紹了Android 自定義view模板并實現(xiàn)點擊事件的回調的相關資料,需要的朋友可以參考下2017-01-01基于Android實現(xiàn)隨手指移動的ImageView
這篇文章主要介紹了基于Android實現(xiàn)隨手指移動的ImageView的相關資料,需要的朋友可以參考下2016-01-01Android集成GreenDao數(shù)據(jù)庫的操作步驟
這篇文章主要介紹了Android集成GreenDao數(shù)據(jù)庫,使用數(shù)據(jù)庫存儲時候,一般都會使用一些第三方ORM框架,比如GreenDao,本文分幾步給大家介紹Android集成GreenDao數(shù)據(jù)庫的方法,需要的朋友可以參考下2022-10-10Flutter實現(xiàn)網(wǎng)絡請求的方法示例
這篇文章主要介紹了Flutter實現(xiàn)網(wǎng)絡請求的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03