본문 바로가기
Rx

안드로이드의 RxJava 활용 - 5( 추천 검색어 기능 구현하기,debounce)

by 봄석 2018. 12. 30.

본 내용은 필자가 학습한 내용을 정리하는 내용입니다.

대부분 의 내용이 아래 책의 내용이므로 원서를 구매해서 직접보시는걸 추천드립니다!

RxJava 프로그래밍 리액티브 프로그래밍 기초부터 RxAndroid까지 한 번에

유동환 , 박정준 지음 | 한빛미디어 | 2017년 09월 04일 출간

http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788968488658&orderClick=LAV&Kc=


저자님의 블로그

https://brunch.co.kr/@yudong#info



실습: 추천 검색어 기능 구현하기

네이버나 구글 검색창에 키워드를 입력하다 보면 추천 검색어를 확인할 수 있습니다.

이번 실습 예제에서는 이벤트 리스너 중 사용 빈도가 높고 유용하게 사용하는 TextChangeEvent()리스너를 이용하여 검새할 키워드를 입력하고 500ms 안에 다른 문자를 입력하지 않으면 검색을 시작하는 코드를 작성합니다.

public class DebounceSearchFragment extends Fragment {
    @BindView(R.id.dsf_input_deb_search)
    EditText mSearchBox;
    @BindView(R.id.dsf_lv_log)
    ListView mLogView;
 
    private LogAdapter mLogAdapter;
    private List<String> mLogs;
 
    private Disposable mDisposable;
    private Unbinder mUnbinder;
 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View layout=inflater.inflate(R.layout.fragment_debounce_search,container,false);
        mUnbinder=ButterKnife.bind(this,layout);
        setupLogger();
        return layout;
    }
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
            mDisposable= getObservable()                
.debounce(500,TimeUnit.MILLISECONDS)
                .filter(s-> !TextUtils.isEmpty(s))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(getObserver());
   }
    private Observable<CharSequence> getObservable(){
        return Observable.create(emitter->mSearchBox.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) {
                emitter.onNext(s);
            }
 
            @Override
            public void afterTextChanged(Editable s) {
 
            }
        }));
    }
    private DisposableObserver<CharSequence>getObserver(){
        return new DisposableObserver<CharSequence>() {
            @Override
            public void onNext(CharSequence charSequence) {
                log("Search :"+charSequence.toString());
            }
 
            @Override
            public void onError(Throwable e) {
 
            }
 
            @Override
            public void onComplete() {
 
            }
        };
    }
 
@Override
    public void onDestroyView() {
        super.onDestroyView();
        if (mUnbinder != null) {
            mUnbinder.unbind();
        }
        mDisposable.dispose();
    }
    private void log(String log) {
        mLogs.add(log);
        mLogAdapter.clear();
        mLogAdapter.addAll(mLogs);
    }
 
    private void setupLogger() {
        mLogs = new ArrayList<>();
        mLogAdapter = new LogAdapter(getActivity(), new ArrayList<>());
        mLogView.setAdapter(mLogAdapter);
    }

}



onActivityCreated() 함수에서는 debounce() 함수와 EditText 클래스의 리스너 이벤트를 Observable에 연결하는 부분을 주의깊게 살펴봐야 합니다.


getObservable() 함수에서는 EditText의 객체인 mSearchBox가 사용자 입력 문자열의 상태 변경을 알 수 있도록 해야합니다.

따라서 addTextChangedListener() 메소드의 인자로 TextWatcher 인터페이스 객체를 전달합니다.


TextWatcher는 beforeTextChanged() ,onTextChanged(), afterTextChanged() 의 3개 메소드를 제공합니다. 여기서는 onTextChanged() 메소드를 사용하여 EditText의 입력 내용을 확인합니다.


사용자가 문자를 입력했을 떄 TextWatcher 객체의 ontextChanged() 메소드가 호출됩니다.

getObservable() 의 발행자(emitter)는 onNext() 함수로 변경된 문자열을 최종 입력받게 되고 debounce()-> filter() 함수 순서로 리액티브 연산자를 처리하게 됩니다. 최종 처리된 문자열은 구독자에게 전달합니다 .




RxView 클래스를 이용하면 Text Watcher 객체가 담당하는 부분을 개발자가 직접 구현 할 필요없이 textChangeEvent() 함수에서 자동으로  처리합니다.

 @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
      
        mDisposable = RxTextView.textChangeEvents(mSearchBox)
                .debounce(400, TimeUnit.MILLISECONDS)
                .filter(s -> !TextUtils.isEmpty(s.text().toString()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(getObserverLib());
    }
    
private DisposableObserver<TextViewTextChangeEvent> getObserverLib(){
        return new DisposableObserver<TextViewTextChangeEvent>() {
            @Override
            public void onNext(TextViewTextChangeEvent textViewTextChangeEvent) {
                log("Search :"+textViewTextChangeEvent.text().toString());
            }
 
            @Override
            public void onError(Throwable e) {
 
            }
 
            @Override
            public void onComplete() {
 
            }
        };
    }


RxTextView.textChageEvents() 에 인자로 EditText 객체인 mSearchBox를 전달하면 내부에서 TextWatcher 객체로 구현한 Observable을 리턴합니다.


마지막으로 눈여겨 봐야 할 부분은 observeOn(AndroidSchedulers.mainThread())입니다 

여기서 구독자의 스레드가 안드로이드의 UI 스레드임을 명시해야합니다.

안드로이드에서 UI 업데이트는 UI스레드에서만 가능하므로 이러한 처리가 필수적입니다.

댓글