Search code examples
androidrx-javarx-binding

RxJava - two Observable sources, combine output only on certain values


Question:

Which operator or transform should I be using to combine two data streams in smallest amount of code and in the most efficient way?

ViewPager, RxBinding, Event stream

With an Android ViewPager, I'm observing events (using RxBinding)

1) OnPageSelected (page currently visible)

Observable<Integer> pageSelectObs = RxViewPager.pageSelections(mPlaceImageViewPager);

and

2) OnPageScrollStateChanged (swipe starting = 1, in motion = 2, complete = 0)

Observable<Integer> scrollStateObs = RxViewPager.pageScrollStateChanges(mPlaceImageViewPager);

The stream of Integers looks like this:

I/System.out: Page: 0 ScrollState: 1
I/System.out: Page: 0 ScrollState: 2
I/System.out: Page: 1 ScrollState: 2
I/System.out: Page: 1 ScrollState: 0
I/System.out: Page: 1 ScrollState: 1
I/System.out: Page: 1 ScrollState: 2
I/System.out: Page: 2 ScrollState: 2
I/System.out: Page: 2 ScrollState: 0

I'm only interested when:

  • ScrollState == 0
  • end of ViewPager is reached

Current Code

This is how I'm currently observing:

Disposable d = ObservableCombineLatest.combineLatest(pageSelectObs, scrollStateObs, new BiFunction<Integer, Integer, Integer>() {
    @Override
    public Integer apply(@NonNull Integer pageSelected, @NonNull Integer scrollState) throws Exception {
        AUtils.logSystem(TAG, "Page: %s ScrollState: %s", pageSelected, scrollState);

        if (adapter.isLastVisibleItem(pageSelected) && adapter.hasHiddenItemsRight() && scrollState == 0) {
            return 1;
        }

        if (adapter.isFirstVisibleItem(pageSelected) && adapter.hasHiddenItemsLeft() && scrollState == 0) {
            return -1;
        }
        return 0;
    }
}).subscribe(new Consumer<Integer>() {
    @Override
    public void accept(@NonNull Integer doAction) throws Exception {
        if (doAction == -1) {
            AUtils.logSystem(TAG, "shift LEFT");
            adapter.shiftLeft();
        }
        if (doAction == 1) {
            AUtils.logSystem(TAG, "shift RIGHT");
            adapter.shiftRight();
        }
    }
});

Is there a simpler way of doing the above?


Solution

  • Since your conditions are quite simple, you can express them with a simple filter() operator.

    Observable<Integer> scrollStateObs = RxViewPager.pageScrollStateChanges(mPlaceImageViewPager)
            .filter(scrollState -> scrollState == ViewPager.SCROLL_STATE_IDLE);
    

    In order to only react on scrollState changes, you can use withLatestFrom() operator

    Disposable d = pageSelectObs.withLatestFrom(scrollStateObs, (pageSelected, scrollState) -> pageSelected)
            .filter(pageSelected -> adapter.isLastVisibleItem(pageSelected));
            .subscribe(pageSelected -> {
                if (adapter.hasHiddenItemsRight()) {
                    adapter.shiftRight();
                } else if (adapter.hasHiddenItemsLeft()) {
                    adapter.shiftRight();
                }
            });