如何使用 RxJava 处理回收站视图的项目点击
How to handle Item clicks for a recycler view using RxJava
我很想知道响应回收站视图的项目点击的最佳方式是什么。
通常我会向 ViewHolder 添加一个 onclick() 侦听器,并通过接口将结果传回 activity/fragment。
我考虑过在 onBindViewHolder 中添加一个 Observable,但我不想为每个项目绑定创建一个新的 Observable。
第 1 步:将业务逻辑从活动移到域 classes/services
可选:使用 https://github.com/roboguice/roboguice 轻松连接您的服务。
第 2 步:@Inject
(或只是将服务设置)到适配器中
第 3 步:抓住 https://github.com/JakeWharton/RxBinding
并使用适配器中的超能力:
RxView.clicks(button).subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
myCoolService.doStuff();
}
});
第 4 步:遇到运行时崩溃并了解如何处理订阅
第 5 步:盈利 :)
我建议您在单击时使用每个元素的可观察对象的初始方法,但为了避免每次都创建一个新的可观察对象,您可以只缓存第一次使用缓存发出的项目。
/**
* 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));
}
您可以使用 RxBinding 然后在您的适配器中创建一个主题,然后将所有事件重定向到该主题并创建一个 getter 该主题作为可观察对象,最后只需在该可观察对象上订阅你即可。
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;
}
用法示例可以是:
mMyAdapter.getViewClickedObservable()
.subscribe(view -> /* do the action. */);
除了使用 EventBus 模型外,我的解决方案与@epool 的非常相似。
首先,创建一个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(); }
}
那么,你有两种方式来使用RxBus。使用 RxBus 引用创建自定义应用程序 class 或在 Activity/Fragment 中创建 RxBus,然后将其传递给适配器。我用的是第一个。
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; }
}
然后使用
MyApp.get().bus()
获取 RxBus 实例。
Adpater中RxBus的用法是这样的:
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(...)); }
});
}
}
您可以使用 _bus.send() 发送任何 class,我们将在 Activity:
中收到
RxBus bus = MyApp.get().bus(); // get the same RxBus instance
_sub = bus.toObserverable()
.subscribe(e -> doSomething((SomeEvent) e));
关于退订。
在 MyRecyclerAdapter 中,在 clearup() 方法中调用 _sub.unsubscribe() 并在 Activity 的 onDestory() 中调用 _sub.unsubscribe()。
@Override
public void onDestroy() {
super.onDestroy();
if (_adapter != null) {
_adapter.cleanup();
}
if (_sub != null) {
_sub.unsubscribe()
}
}
我们通常需要 Pojo/Model class 来自点击索引的列表。我按照以下方式进行:
1) 创建一个 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) 在任何适配器中(例如 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) 在需要点击监听器的 Activity
或 Fragment
中
myAdapter?.observeClickListener()
?.subscribe({ eventClicked ->
// do something with clicked item
})
我很想知道响应回收站视图的项目点击的最佳方式是什么。
通常我会向 ViewHolder 添加一个 onclick() 侦听器,并通过接口将结果传回 activity/fragment。
我考虑过在 onBindViewHolder 中添加一个 Observable,但我不想为每个项目绑定创建一个新的 Observable。
第 1 步:将业务逻辑从活动移到域 classes/services
可选:使用 https://github.com/roboguice/roboguice 轻松连接您的服务。
第 2 步:@Inject
(或只是将服务设置)到适配器中
第 3 步:抓住 https://github.com/JakeWharton/RxBinding
并使用适配器中的超能力:
RxView.clicks(button).subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
myCoolService.doStuff();
}
});
第 4 步:遇到运行时崩溃并了解如何处理订阅
第 5 步:盈利 :)
我建议您在单击时使用每个元素的可观察对象的初始方法,但为了避免每次都创建一个新的可观察对象,您可以只缓存第一次使用缓存发出的项目。
/**
* 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));
}
您可以使用 RxBinding 然后在您的适配器中创建一个主题,然后将所有事件重定向到该主题并创建一个 getter 该主题作为可观察对象,最后只需在该可观察对象上订阅你即可。
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;
}
用法示例可以是:
mMyAdapter.getViewClickedObservable()
.subscribe(view -> /* do the action. */);
除了使用 EventBus 模型外,我的解决方案与@epool 的非常相似。
首先,创建一个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(); }
}
那么,你有两种方式来使用RxBus。使用 RxBus 引用创建自定义应用程序 class 或在 Activity/Fragment 中创建 RxBus,然后将其传递给适配器。我用的是第一个。
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; }
}
然后使用
MyApp.get().bus()
获取 RxBus 实例。
Adpater中RxBus的用法是这样的:
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(...)); }
});
}
}
您可以使用 _bus.send() 发送任何 class,我们将在 Activity:
中收到RxBus bus = MyApp.get().bus(); // get the same RxBus instance
_sub = bus.toObserverable()
.subscribe(e -> doSomething((SomeEvent) e));
关于退订。
在 MyRecyclerAdapter 中,在 clearup() 方法中调用 _sub.unsubscribe() 并在 Activity 的 onDestory() 中调用 _sub.unsubscribe()。
@Override
public void onDestroy() {
super.onDestroy();
if (_adapter != null) {
_adapter.cleanup();
}
if (_sub != null) {
_sub.unsubscribe()
}
}
我们通常需要 Pojo/Model class 来自点击索引的列表。我按照以下方式进行:
1) 创建一个 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) 在任何适配器中(例如 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) 在需要点击监听器的 Activity
或 Fragment
中
myAdapter?.observeClickListener()
?.subscribe({ eventClicked ->
// do something with clicked item
})