Search code examples
androidrx-javarx-binding

RxJava Observable to generate repeated events when button is down


I'm trying to write an observable that would generate repeated events while the user holds down a view. My code below works well, but only the first time (e.g. if the user presses the button again, nothing happens). Can you please advise what am I doing wrong and what is best practice for this?

val touches = RxView.touches(previousButton)
touches
        .filter({ event -> event.action == MotionEvent.ACTION_DOWN })
        .flatMap({
            Observable.interval(500, 50, TimeUnit.MILLISECONDS)
                    .takeUntil(touches.filter({event -> event.action == MotionEvent.ACTION_UP}))
        }).subscribe({ println("down") })

Solution

  • The problem is that the RxView.touches observable cannot exist for more than 1 source. This means when the subscription inside of the flatMap happens it breaks the original subscription used to trigger the flatMap, making it never occur again.

    There are two possible ways around this:

    1. Use .publish(...) to share the source of events instead of using touches.
    2. Map the events into a Boolean on/off observable, then switchMap the appropriate actions based on the current value of the observable.

    1.

    touches.publish { src ->
        src.filter(...)
           .flatMap {
               Observable.interval(...)
                         .takeUntil(src.filter(...))
           }
    }
    

    2.

    touches.filter {
               it.action == MotionEvent.ACTION_DOWN 
                    or it.action == MotionEvent.ACTION_UP
           }
           .map { it.action == MotionEvent.ACTION_DOWN }
           .distinctUntilChanged() // Avoid repeating events
           .switchMap { state ->
               if (state) {
                   Observable.interval(...)
               } else {
                   Observable.never()
               }
           }