Je souhaitais savoir quel est le meilleur moyen de répondre à un clic sur un élément d'une vue de recycleur.
Normalement, je voudrais ajouter un auditeur onclick () au ViewHolder et transmettre les résultats à l'activité/au fragment via une interface.
J'ai pensé à ajouter un observable dans le onBindViewHolder mais je ne veux pas créer un nouvel observable pour chaque liaison d'éléments.
Vous pouvez utiliser RxBinding puis créer un sujet à l'intérieur de votre adaptateur, puis rediriger tous les événements vers ce sujet et créer un getter du sujet pour qu'il puisse agir en tant qu'observable et pour finalement vous abonner à cet observable.
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 exemple d'utilisation pourrait être:
mMyAdapter.getViewClickedObservable()
.subscribe(view -> /* do the action. */);
Ma solution ressemblait beaucoup à celle de @epool, à l'exception du modèle EventBus.
Tout d’abord, créez 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(); }
}
Ensuite, vous avez deux façons d'utiliser RxBus. Créez votre classe d'application personnalisée avec une référence RxBus ou créez RxBus dans Activity/Fragment puis transmettez-la à l'adaptateur. Je me sers du premier.
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; }
}
puis utiliser
MyApp.get().bus()
pour obtenir l'instance RxBus.
L'utilisation de RxBus dans Adpater était la suivante:
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(...)); }
});
}
}
Vous pouvez envoyer n'importe quelle classe avec _bus.send (), que nous recevrons dans l'activité:
RxBus bus = MyApp.get().bus(); // get the same RxBus instance
_sub = bus.toObserverable()
.subscribe(e -> doSomething((SomeEvent) e));
À propos de la désinscription.
Dans MyRecyclerAdapter, appelez _sub.unsubscribe () dans les méthodes clearup () et appelez _sub.unsubscribe () dans Activity's onDestory ().
@Override
public void onDestroy() {
super.onDestroy();
if (_adapter != null) {
_adapter.cleanup();
}
if (_sub != null) {
_sub.unsubscribe()
}
}
Étape 1: déplacez la logique métier des activités vers des classes/services de domaine
Facultatif: Utilisez https://github.com/roboguice/roboguice pour connecter facilement vos services les uns avec les autres.
Étape 2: @Inject
(ou définissez simplement) votre service dans votre adaptateur
Étape 3: Saisissez https://github.com/JakeWharton/RxBinding
et utilisez les super pouvoirs de votre adaptateur:
RxView.clicks(button).subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
myCoolService.doStuff();
}
});
Étape 4: obtenez une panne d'exécution et apprenez à gérer les abonnements
Étape 5: PROFIT :)
Je vous suggère de faire avec votre approche initiale d'une observable par élément sur un clic, mais afin d'éviter de créer une nouvelle observable à chaque fois, vous pouvez simplement mettre en cache les éléments émis la première fois en utilisant 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));
}
Nous avons généralement besoin de la classe Pojo/Model de la liste sur l’index cliqué. Je le fais de la manière suivante:
1) Créer une 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) Dans n’importe quel adaptateur (par exemple 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) À l'intérieur de la Activity
ou Fragment
où un écouteur de clic est nécessaire
myAdapter?.observeClickListener()
?.subscribe({ eventClicked ->
// do something with clicked item
})