How to create an Observable from OnClick Event Android?
Asked Answered
A

5

41

I'm new in reactive programming. So I have problem when create a stream from an Event, like onClick, ontouch...

Can anyone help me solve this problem.

Thanks.

Assay answered 23/8, 2014 at 1:53 Comment(0)
E
57

You would do something like this:

Observable<View> clickEventObservable = Observable.create(new Observable.OnSubscribe<View>() {
    @Override
    public void call(final Subscriber<? super View> subscriber) {
        viewIWantToMonitorForClickEvents.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (subscriber.isUnsubscribed()) return;
                subscriber.onNext(v);
            }
        });
    }
});

// You can then apply all sorts of operation here
Subscription subscription = clickEventObservable.flatMap(/*  */);

// Unsubscribe when you're done with it
subscription.unsubscribe();

Since you're using Android then you may already include the contrib rxjava-android dependency now known as ioreactivex:rxandroid. They already have a class to facilitate this. The method is ViewObservable.clicks. You can use it like so.

Observable<View> buttonObservable = ViewObservable.clicks(initiateButton, false);
    buttonObservable.subscribe(new Action1<View>() {
        @Override
        public void call(View button) {
            // do what you need here
        }
    });

Edit: Since version 1.x, ViewObservable and many helper classes are removed from RxAndroid. You will need RxBinding library instead.

Observable<Void> buttonObservable = RxView.clicks(initiateButton);
    buttonObservable.subscribe(new Action1<Void>() {
        @Override
        public void call(Void x) {
            // do what you need here
        }
    });
Epitome answered 23/8, 2014 at 2:21 Comment(0)
L
28

For Android development, have a look at Jake Wharton's RxBindings. For example, it allows you to create an observable and subscribe to click events with:

RxView.clicks(myButton)
        .subscribe(new Action1<Void>() {
            @Override
            public void call(Void aVoid) {
                /* do something */
            }
        });

or, even better, with lambda expressions (using either Kotlin, Java 8 or Retrolambda):

RxView.clicks(myButton)
        .subscribe(aVoid -> /* do something */);

If you're using Kotlin, it's worth noting that RxBindings also provides Kotlin extension functions that allow you to apply each binding function directly on the target type.

Lederman answered 11/11, 2015 at 14:10 Comment(1)
Error: lambda expressions are not supported in -source 1.7Leavenworth
T
17

You could use a Subject.

A Subject is a sort of bridge or proxy that acts both as an Subscriber and as an Observable. Because it is a Subscriber, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.

public class Events {
    public static PublishSubject <Object> myEvent = PublishSubject.create ();
}

When you want to publish something

Events.myEvent.onNext(myObject);

When you want to receive an event

Events.myEvent.subscribe (...);

Edit

**Using Architecture Components LiveData is better because it handles the lifecycle of and activity or fragment and you don't have to worried about unsubscribe from events because it observe the ui components lifecycle.

MutableLiveData<Object> event = new MutableLiveData<>();

when you want to publish something

event.postValue(myObject);

When you want to receive and event

event.observe(lifeCycleOwner, (myObject)->...);
Trichloride answered 23/8, 2014 at 2:27 Comment(0)
M
14

Using RxJava 2:

return Observable.create(new ObservableOnSubscribe<View>() {
    @Override
    public void subscribe(ObservableEmitter<View> e) throws Exception {
        clickView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                e.setCancellable(new Cancellable() {
                    @Override
                    public void cancel() throws Exception {
                        view.setOnClickListener(null);
                    }
                });
                e.onNext(view); // Or whatever type you're observing
            }
        });
    }
});

Looks much nicer using lambdas:

return Observable.create(new ObservableOnSubscribe<View>() {
    @Override
    public void subscribe(ObservableEmitter<View> e) throws Exception {
        keypad.setOnClickListener(view -> {
            e.setCancellable(() -> view.setOnClickListener(null));
            e.onNext(view);
        });
    }
});

Or just use RxBinding as stated by others.

My solution above is a pretty general pattern for wrapping listeners into an Observable though.

Mellon answered 21/11, 2016 at 13:36 Comment(3)
Why do we need to call e.setCancellable() here? I can't find docs. What the difference comparing with e.setDisposable()?Thurifer
@Thurifer To see the differences between Cancellable and Disposable check this answer https://mcmap.net/q/392379/-what-is-the-difference-between-rxjava-2-cancellable-and-disposableSoni
When i call e.setCancellable(() -> view.setOnClickListener(null)); button react to touches 2 times and on second touch it calls what is inside e.setCancellable. Why does this happen?Among
S
1

You can do this easily with Kotlin using extension functions. For example you write an extension function like this:

    fun View.observableClickListener(): Observable<View> {
    val publishSubject: PublishSubject<View> = PublishSubject.create()
    this.setOnClickListener { v -> publishSubject.onNext(v) }
    return publishSubject
}

And you would use this extension like this:

viewIWantToObserveClicks.observableClickListener().subscribe()
Sami answered 23/7, 2019 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.