본문 바로가기
Rx

안드로이드의 RxJava 활용 - 3( UI 이벤트처리)

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




UI 이벤트 처리

안드로이드는 사용자가 애플리케이션과 상호 작용할 때 발생하는 특정 View 객체의 이벤트를 얻는 방법을 제공합니다. 따라서 View 클래스 안에는 UI이벤트를 처리하기 위한 몇 가지 콜백베소드가 있습니다. 이벤트 리스너 라고 하는 인터페이스 모음입니다.

콜백 메소드 이름 

설명 

 onClick() 

View.OnClickListenter에서 콜백함. 사용자가 아이템을 터치하거나 내비게이션 키 혹은 트랙볼로 해당 아이템을 포커스하여 [enter] 키 혹은 트랙볼을 눌렀을 때 호출합니다.

 onLongClick() 

View.OnFocusChangeListener에서 콜백함. 사용자가 아이템을 길게 터치하거나 내비게이션 키 혹은 트랙볼로 해당 아이템을 포커스하여 [enter] 키 혹은 트랙볼(1초 이상)을 길게 눌렀을 때 호출합니다. 

 onFocusChange()  

View.OnFocusChangeListner에서 콜백함. 사용자가 내비게이션 키 또는 트랙볼을 사용하여 아이템을 위로 움직이게 하거나 포커스가 벗어날 떄 호출합니다.

 onKey()  

View.OnKeyListener에서 콜백함. 사용자가 아이템을 포커스한 후 디바이스에 있는 키를 누르거나 놓았을때 호출합니다.

 onTouch() 

View.OnTouchListener에서 콜백함. 사용자가 아이템 경계 안에서 스크린을 누르거나 놓거나 어떤 움직임을 포함하는 터치 이벤트 액션을 실행할때 호출합니다. 

 onCreateContextMenu() 

View.OnCreateContectMenuListener에서 콜백함. 길게 터치하거나 누른 결과로 컨텍스트 메뉴가 열렸을때 호출합니다. 



이벤트 리스너 안에 포함된 onClick() 메소드에 Observable을 활용한 예

public class OnClickFragment extends Fragment {
    public static final String TAG = OnClickFragment.class.getSimpleName();
 
    @BindView(R.id.btn_click_observer)
    Button mButton;
    
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
 
        getClickEventObservable()
                .map(s -> "clicked")
                .subscribe(getObserver());
    }
 
    private Observable<View> getClickEventObservable() {
        return Observable.create(new ObservableOnSubscribe<View>() {
            @Override
            public void subscribe(ObservableEmitter<View> e) throws Exception {
                mButton.setOnClickListener(e::onNext);
            }
        });
    }
    
    private Observable<View> getClickEventObservableWithLambda() {
        return Observable.create(s -> mButtonLambda.setOnClickListener(s::onNext));
    }
    private DisposableObserver<super String> getObserver() {
        return new DisposableObserver<String>() {
            @Override
            public void onNext(String s) { log(s); }
 
            @Override
            public void onError(Throwable e) { log(e.getMessage()); }
 
            @Override
            public void onComplete() { log("complete"); }
        };
    }
}




동일한 동작을 하는 세가지 Observable을 작성했습니다 .mButton 에 해당하는 아이템을 클릭하면 setOnClickListener() 메소드가 호출되고 메소드 레퍼런스인 e::onNext를 호출합니다.

클릭한 아이템이 있는 View 정보를 전달하면 map() 함수는 'clicked' 라는 String 값으로 변경합니다. 그럼 리턴 값이 Observable<View> 에서 Observable<String>으로 변경되고 옵서버는 clicked를 출력합니다.


subscribe() 함수의 인자로 View 객체를 전달하는 이 부분의 목적에 따라 연산자를 이용하여 다양하게 변경할 수 있습니다.


subscribe() 함수의 인자 변경 예 1

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
 
        getClickEventObservableWithLamda()
                .map(s->"clicked lamda")
                .subscribe(this::log);
    }
 private Observable<View> getClickEventObservableWithLamda(){
        return Observable.create(s->mButtonLambda.setOnClickListener(s::onNext));
    }




리액티브 프로그래밍에 익숙하여서 람다표현식 혹은 Sream API를 이미 사용한다면

코드를 좀더 간결하게 작성할 수 있습니다.

@BindView(R.id.btn_click_observer_binding)
    Button mButtonBinding;
 
@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
 
        getClickEventObservableWithRxBinding().subscribe(this::log);    
}
 private Observable<String> getClickEventObservableWithRxBinding(){
        return RxView.clicks(mButtonBinding)
                .map(s->"Clicked Rxbinding");
}




RxView 클래스를 이용한 방법으로, RxView 객체를 사용하면 Observable의 명시적 생성이 필요없으며, 클릭 리스너 설정도 RxView 내부에서 자동 처리되어 코드가 더욱 직관적으로 변하고 가독성도 향상됩니다.


private static final int SEVEN = 7;
 
@BindView(R.id.btn_click_observer_extra)
    Button mButtonExtra;
 
@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
 
         getClickEventObservableExtra()
                .map(local -> SEVEN)
                .flatMap(this::compareNumbers)
                .subscribe(this::log); 
}
private Observable<String> compareNumbers(int input){
        return Observable.just(input)
                .flatMap(in->{
                    Random random=new Random();
                    int data =random.nextInt(10);
                    return Observable.just("local :"+in,"remote :"+
                    data,"result = "+(in==data));
                });
}


이번에는 위의 코드를 확장해서, 미국에있는 서버키와 로컬에 있는 키가 같은값인지 확인하고 그 결과를 정보별로 나눠서 출력합니다.


getClickEventObservableExtra() 함수에 있는 로컬 변수 SEVEN은 로컬에서 확인이 필요한 키입니다. compareNumbers() 메소드에서 Random() 함수가 발행하는 숫자는 서버에 저장된 키입니다. 참고로 Random() 함수는 REST API로 구현해야하는 내용을 임의로 표현한 것입니다.


map() 함수는 나에게 있는 키를 flatMap() 함수로 전달합니다. 또 다른 Observable은 해당 키값을 새로 생성한 값과 비교하여 결과를 3개의 아이템으로 만들어 전달합니다.


위코드는 실험정신을 발휘한 코드라 수정의 여지는 있지만 리액티브 프로그래밍의 방법을 잘 보여주는 예제입니다. 리액티브 프로그래밍은 Observable과 Observable의 관계를 정의해 가는 것입니다. Observable을 생성해 데이터를 처리하고 결과를 다른 Observable과 조합해가며 조금씩 프로그램의 틀을 완성한다는 사실을 꼭 기억해두기 바랍니다.

댓글