2017-04-25 7 views
0

Question:

quel opérateur ou transformer devrais-je utiliser pour combiner deux flux de données en plus petite quantité de code et de la manière la plus efficace?RxJava - deux sources Observable, combiner sortie uniquement sur certaines valeurs

ViewPager, RxBinding, flux d'événements

Avec un Android ViewPager, j'observe les événements (en utilisant RxBinding)

1) OnPageSelected (page actuellement visible)

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

et

2) OnPageScrollStateChanged (balayage de départ = 1, en mouvement = 2, complet = 0)

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

Le flux de Entiers ressemble à ceci:

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 

Je suis seulement intéressé lorsque:

  • ScrollState == 0
  • fin de ViewPager est atteinte
Code service Code commande

Code actuel

Voilà comment je suis en train d'observer:

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(); 
     } 
    } 
}); 

est-il un moyen plus simple de faire ce qui précède?

+0

Pourquoi vérifiez-vous ceci: adapter.isLastVisibleItem (pageSelected) && adapter.hasHiddenItemsRight()? –

+0

Je change l'ensemble de données en fonction de la page. – Baker

Répondre

1

Étant donné que vos conditions sont assez simples, vous pouvez les exprimer avec un simple opérateur. filter()

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

Afin de réagir uniquement sur scrollState changements, vous pouvez utiliser withLatestFrom() opérateur

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(); 
      } 
     }); 
+0

Exactement ce que je cherchais. Merci.1ère question: Lors du filtrage des états de défilement sauf SCROLL_STATE_IDLE, ViewPager coupe la phase SETTLING et place la page suivante en vue (plutôt que de s'installer doucement). Des idées pour lesquelles cela se produit avec des flux filtrés mais pas avec ma solution originale et brute? – Baker

+0

2ème question: 'withLatestFrom', la BiFunction renvoie simplement pageSelected et ne fait rien d'autre, n'est-ce pas? (En utilisant RxJava2 et je n'utilise pas d'instructions Lambda dans mon code, parfois je ne sais pas ce qui est caché.) – Baker

+0

1. Je ne comprends pas vraiment 2. Oui, mais peu importe ce que vous y retournez comme il n'est pas utilisé – Lamorak

0

Eh bien «le moyen le plus efficace» dépend de votre exigence et de la façon dont vous définissez le plus efficace. Est-ce le temps, est-ce que c'est des ressources?

J'ai pris votre code et ajouté une fenêtre à débit limité de 50 msec, que les événements bursty n'appellent pas trop souvent.

Dans l'opérande MAP, vous devez ajouter une correspondance à l'enum, car -1 et 1 ne sont pas des valeurs représentatives.

@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); 
} 

: Structures des données

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 
}