Reputation: 28080
Which operator or transform should I be using to combine two data streams in smallest amount of code and in the most efficient way?
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:
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?
Upvotes: 0
Views: 837
Reputation: 11137
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();
}
});
Upvotes: 1
Reputation: 4012
Well 'the most efficient way' depends on your requirment and how you define most efficient. Is it time, is it resources?
I took your code and added a rate-limited-window of 50 msec, that bursty events would not call onNext too often.
In the MAP-opreator you would add some matching to the enum, because -1 and 1 are not representative values.
@Test
void name() throws Exception {
Observable<Integer> pageSelectObs = Observable.just(0, 0, 1, 1, 1, 1, 2, 2);
Observable<Integer> scrollStateObs = Observable.just(1, 2, 2, 0, 1, 2, 2, 0);
// Test-Obs
Observable<ShiftOperation> filter = Observable.combineLatest(pageSelectObs, scrollStateObs, Combined::new)
.window(50, TimeUnit.MILLISECONDS)
.flatMap(combinedObservable -> combinedObservable.filter(combined -> combined.scrollState == 0)
.takeLast(1))
.map(combined -> {
// do mapping here
return ShiftOperation.SHIFT_LEFT; // do your adapter... check here and decide which operation you want to return.
});
filter.test()
.await()
.assertValueCount(1);
}
Datastructures:
class Combined {
final int pageState;
final int scrollState;
Combined(int pageState, int scrollState) {
this.pageState = pageState;
this.scrollState = scrollState;
}
}
enum ShiftOperation {
SHIFT_LEFT,
SHIFT_RIGHT
}
Upvotes: 0