2016-04-08 28 views
9

Ero interessato a scoprire qual è il modo migliore per rispondere a un clic di un elemento di una visualizzazione riciclatore.Come gestire i clic degli articoli per una visualizzazione riciclatore utilizzando RxJava

Normalmente aggiungo un listener onclick() al ViewHolder e restituisco i risultati all'attività/al frammento tramite un'interfaccia.

Ho pensato di aggiungere un Observable in onBindViewHolder ma non voglio creare un nuovo Observable per ogni elemento vincolante.

+2

È possibile utilizzare un Publis hSubject. Quindi lo crei solo una volta e ti basta emettere dati su di esso, pur avendo sottoscritto una sola volta. – Pavlos

risposta

10

È possibile utilizzare RxBinding e quindi creare un soggetto all'interno della scheda, quindi reindirizzare tutti gli eventi di quel soggetto e basta creare un getter del soggetto di agire come un osservabile e, infine, solo voi iscriversi su quella osservabile .

private PublishSubject<View> mViewClickSubject = PublishSubject.create(); 

public Observable<View> getViewClickedObservable() { 
    return mViewClickSubject.asObservable(); 
} 

@Override 
public ViewHolder onCreateViewHolder(@NonNull ViewGroup pParent, int pViewType) { 
    Context context = pParent.getContext(); 
    View view = (View) LayoutInflater.from(context).inflate(R.layout.your_item_layout, pParent, false); 
    ViewHolder viewHolder = new ViewHolder(view); 

    RxView.clicks(view) 
      .takeUntil(RxView.detaches(pParent)) 
      .map(aVoid -> view) 
      .subscribe(mViewClickSubject); 

    return viewHolder; 
} 

Un esempio di utilizzo potrebbe essere:

mMyAdapter.getViewClickedObservable() 
     .subscribe(view -> /* do the action. */); 
+3

Dove annulli l'iscrizione? – EpicPandaForce

+0

Yo può gestire quello in cui si effettua l'abbonamento. Probabilmente si sta utilizzando una Composibilità se si dispone di più di una sottoscrizione. – epool

+1

se è necessario solo osservabile (in generale!) - è necessario chiamare mViewClickSubject.asObservable(). altrimenti ogni sottoscrittore può chiamare suErro() (ad esempio, che non è ok! –

0

Fase 1: Spostare la logica di business delle attività per classi di dominio/servizi

Optional: impiego https://github.com/roboguice/roboguice cablare facilmente i vostri servizi con l'altro.

Fase 2: @Inject (o solo set) il servizio nel vostro adattatore

Fase 3: Grab https://github.com/JakeWharton/RxBinding e utilizzare i super poteri nella scheda:

RxView.clicks(button).subscribe(new Action1<Void>() { 
    @Override 
    public void call(Void aVoid) { 
     myCoolService.doStuff(); 
    } 
}); 

Fase 4: Ottenere un incidente di esecuzione e imparare a trattare con gli abbonamenti

Fase 5: UTILE :)

0

io vi suggerisco di fare con la vostra iniziale aproach di un osservabile per elemento al click, ma per evitare di creare un nuovo osservabile ogni volta che si può semplicemente memorizzare nella cache gli oggetti emessi la prima volta utilizzando la cache.

 /** 
* Here we can prove how the first time the items are delayed 100 ms per item emitted but second time becuase it´s cached we dont have any delay since 
* the item emitted are cached 
*/ 
@Test 
public void cacheObservable() { 
    Integer[] numbers = {0, 1, 2, 3, 4, 5}; 

    Observable<Integer> observable = Observable.from(numbers) 
               .doOnNext(number -> { 
                try { 
                 Thread.sleep(100); 
                } catch (InterruptedException e) { 
                 e.printStackTrace(); 
                } 
               }) 
               .cache(); 
    long time = System.currentTimeMillis(); 
    observable.subscribe(System.out::println); 
    System.out.println("First time took:" + (System.currentTimeMillis() - time)); 
    time = System.currentTimeMillis(); 
    observable.subscribe(System.out::println); 
    System.out.println("Second time took:" + (System.currentTimeMillis() - time)); 

} 
0

La mia soluzione era molto simile a s @epool' tranne modello di utilizzo EventBus.

In primo luogo, creare un RxBus: RxBus.java

public class RxBus { 
    private final Subject<Object, Object> _bus = new SerializedSubject<>(PublishSubject.create()); 
    public void send(Object o) { _bus.onNext(o); } 
    public Observable<Object> toObserverable() { return _bus; } 
    public boolean hasObservers() { return _bus.hasObservers(); } 
} 

Allora, avete due modi per utilizzare RxBus. Crea la tua classe Application personalizzata con riferimento RxBus o crea RxBus in Activity/Fragment e poi passa ad adapter. Io uso il primo

MyApp.java

public class MyApp extends Application { 
    private static MyApp _instance; 
    private RxBus _bus; 
    public static MyApp get() { return _instance; } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     _instance = this; 
     _bus = new RxBus(); 
    } 

    public RxBus bus() { return _bus; } 
} 

quindi utilizzare

MyApp.get().bus() 

per ottenere un'istanza RxBus.

L'utilizzo di RxBus in Adpater era come questo:

public class MyRecyclerAdapter extends ... { 
    private RxBus _bus; 

    public MykRecyclerAdapter (...) { 
     .... 
     _bus = MyApp.get().bus(); 
    } 

    public ViewHolder onCreateViewHolder (...) { 
     _sub = RxView.longClicks(itemView) // You can use setOnLongClickListener as the same 
       .subscribe(aVoid -> { 
         if (_bus.hasObservers()) { _bus.send(new SomeEvent(...)); } 
        });  
    } 
} 

è possibile inviare qualsiasi classe con _bus.send(), che riceveremo nell'attività:

RxBus bus = MyApp.get().bus(); // get the same RxBus instance 
_sub = bus.toObserverable() 
      .subscribe(e -> doSomething((SomeEvent) e)); 

Informazioni sull'iscrizione.

In MyRecyclerAdapter, chiamare _sub.unsubscribe() nei metodi clearup() e chiamare _sub.unsubscribe() in Activity onDestory().

@Override 
public void onDestroy() { 
    super.onDestroy(); 
    if (_adapter != null) { 
     _adapter.cleanup(); 
    } 
    if (_sub != null) { 
     _sub.unsubscribe() 
    } 
} 
0

Abbiamo generalmente bisogno della classe Pojo/modello dall'elenco sull'indice cliccato. Io lo faccio in modo seguente:

1) Creare un BaseRecyclerViewAdapter

abstract class BaseRecyclerViewAdapter<T> : RecyclerView.Adapter<RecyclerView.ViewHolder>() { 
    private val clickListenerPublishSubject = PublishSubject.create<T>() 

    fun observeClickListener(): Observable<T> { 
     return clickListenerPublishSubject 
    } 

    fun performClick(t: T?) { 
     t ?: return 
     clickListenerPublishSubject.onNext(t) 
    } 
} 

2) In ogni adattatore (ad esempio MyAdapter)

class MyAdapter(private val events: List<Event>, context: Context) : BaseRecyclerViewAdapter<Event>() { 
    //... Other methods of RecyclerView 
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { 
     if (holder is EventViewHolder) { 
      holder.eventBinding?.eventVm = EventViewModel(events[position]) 

      holder.eventBinding?.root?.setOnClickListener({ _ -> 
       // This notifies the subscribers 
       performClick(events[position]) 
      }) 
     } 
    } 
} 

3) All'interno della Activity o Fragment dove cliccare ascoltatore è necessario

myAdapter?.observeClickListener() 
       ?.subscribe({ eventClicked -> 
        // do something with clicked item 

       })