我在Android上,我创建了一个Observable
触摸屏。只要新事件在一秒钟内到来,我想缓冲它们。当从最后一次触摸发射开始经过一秒钟时,我想创建一个所有收集事件的列表并发出它,并开始一个新的集合。
我有两段代码可以使用,但它们要么不太活跃(#1)或不必要的复杂(#2)。他们在这里:
buffer(Func0<? extends Observable<? extends TClosing>> bufferClosingSelector)
重载 - 当返回的选择器发出一个项目时,它意味着从最后一个源发射开始经过了第二个,并且可以发出缓冲区。Obserbable
是PublishSubject
,以便我可以决定何时向其推送排放。Runnable
任务,将新排放推向最终主题。此任务被安排(通过Android处理程序)在当前处理的源触发发射发生后运行一秒。如果在一秒钟之后发生新触发的新源发射,则该任务将被取消,并且在一秒钟后再次安排新的任务。以下是相关的Android代码:
final PublishSubject<MotionEvent> touchPublishSubject = PublishSubject.create();
final ViewGroup viewGroup = (ViewGroup) findViewById(android.R.id.content);
viewGroup.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
touchPublishSubject.onNext(event);
return true;
}
});
final PublishSubject<Object> windowClosePublishSubject = PublishSubject.create();
final Handler handler = new Handler(Looper.getMainLooper());
final Runnable r = new Runnable() {
@Override
public void run() {
Log.d(TAG, "will emit closing item");
windowClosePublishSubject.onNext("now!");
}
};
ViewObservable
.bindView(viewGroup, touchPublishSubject)
.filter(new Func1<MotionEvent, Boolean>() {
@Override
public Boolean call(MotionEvent motionEvent) {
return motionEvent.getAction() == MotionEvent.ACTION_DOWN;
}
})
.doOnNext(new Action1<MotionEvent>() {
@Override
public void call(MotionEvent motionEvent) {
// restart the timer
Log.d(TAG, "cancelling closing");
handler.removeCallbacks(r);
Log.d(TAG, "scheduling closing");
handler.postDelayed(r, 1000L);
// show the touch
Log.d(TAG, motionEvent.toString());
}
})
.buffer(new Func0<Observable<?>>() {
@Override
public Observable<?> call() {
Log.d(TAG, "creating buffer closing selector");
return windowClosePublishSubject
.doOnNext(new Action1<Object>() {
@Override
public void call(Object o) {
Log.d(TAG, "emitting closing item '" + o + "'");
}
});
}
})
.subscribe(new Action1<List<MotionEvent>>() {
@Override
public void call(List<MotionEvent> motionEvents) {
// show number of touch downs
Log.d(TAG, "got " + motionEvents.size() + " touch downs");
}
});
我不喜欢Handler
的使用以及此解决方案中的所有内容,所以我进一步了解。
第二个片段(touchPublishSubject和触控侦听器完全相同):
touchPublishSubject
作为关闭可观察的窗口,首先以1秒的超时时间将其展开Scheduler.computation()
上进行去抖动时,它会将观察移动到同一个调度程序,我需要使用observeOn(AndroidSchedulers.mainThread())
- 我觉得嵌套Observable
&#有点奇怪39; s调度程序,只关闭缓冲区窗口,
促进整个链条也在其调度程序中发生代码:
final PublishSubject<MotionEvent> touchPublishSubject = PublishSubject.create();
final ViewGroup viewGroup = (ViewGroup) findViewById(android.R.id.content);
viewGroup.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
touchPublishSubject.onNext(event);
return true;
}
});
ViewObservable
.bindView(viewGroup, touchPublishSubject)
.filter(new Func1<MotionEvent, Boolean>() {
@Override
public Boolean call(MotionEvent motionEvent) {
return motionEvent.getAction() == MotionEvent.ACTION_DOWN;
}
})
.doOnNext(new Action1<MotionEvent>() {
@Override
public void call(MotionEvent motionEvent) {
// show the touch
Log.d(TAG, motionEvent.toString());
}
})
.buffer(new Func0<Observable<?>>() {
@Override
public Observable<?> call() {
Log.d(TAG, "creating buffer closing selector");
return touchPublishSubject
.debounce(1L, TimeUnit.SECONDS)
.doOnNext(new Action1<Object>() {
@Override
public void call(Object o) {
Log.d(TAG, "emitting closing item '" + o + "'");
}
});
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<MotionEvent>>() {
@Override
public void call(List<MotionEvent> motionEvents) {
// show number of touch downs
Log.d(TAG, "got " + motionEvents.size() + " touch downs");
}
});
此代码有效,我比第一个更喜欢它,感觉更像是应该使用Rx。但它很复杂,因为嵌套Observable
和获得它所需的大脑体操。是否有一个buffer
重载我没有自动执行相同操作(即在最后一次发射后1秒钟关闭其窗口)?
编辑:其中一条评论让我了解了Ben Christensen的演示文稿,然后我发现了这个:https://blog.kaush.co/2015/01/05/debouncedbuffer-with-rxjava/,它链接到了一些问题的实现。看起来像一个非常常见的要求,为此有一个内置的运算符会很好。无论如何,我会考虑在这些其他来源中提出的解决方案,并且这里是针对此类问题的正典。