使用Observable检测单击与双击:Buffer操作符异常问题问询
Rx.NET 正确识别单击与双击事件流
我有一个IObservable<Click>点击事件流,想要从中区分用户的单击或双击操作。尝试用指定doubleClickTime(如300ms)的Buffer操作符缓冲事件,通过计数判断操作类型,但双击时会同时输出Double Click和Single Click,即同时触发双击和单击逻辑。
现有实现代码:
IObservable<Click> clicks = ...; IDisposable subscription = clicks.Buffer(clicks, _ => Observable.Timer(doubleClickTime)) .Subscribe(items => { if(items.Count > 1) { Debug.WriteLine($"Double Click"); } else { Debug.WriteLine($"Single Click"); } });
问题原因:原代码用每次点击作为缓冲窗口的开启信号,双击时第二次点击会开启新的缓冲窗口,导致第一个窗口包含两次点击(输出双击),第二个窗口仅包含第二次点击(输出单击)。
补充:曾参考RxJS的实现示例,但无法准确转译为C#:
const clickOrDoubleClick$ = click$.pipe( buffer(click$.pipe(switchMap(() => timer(DOUBLE_CLICK_INTERVAL)))), map((clicksArr) => clicksArr.length > 1 ? createDoubleClick() : createClick() ));
解决方案
方法一:转译RxJS示例到C#
对应RxJS的实现逻辑,使用Select+Switch替代RxJS的switchMap,确保每次新点击都会取消之前的定时器,仅保留最新的缓冲窗口:
var doubleClickTime = TimeSpan.FromMilliseconds(300); IObservable<string> clickOrDoubleClick = clicks .Buffer( clicks.Select(_ => Observable.Timer(doubleClickTime)) .Switch() // 每次新点击切换到新定时器,旧定时器被取消 ) .Select(items => items.Count > 1 ? "Double Click" : "Single Click"); clickOrDoubleClick.Subscribe(result => Debug.WriteLine(result));
方法二:使用GroupByUntil分组处理
将指定时间窗口内的点击归为一组,超时后关闭分组并判断点击次数:
var doubleClickTime = TimeSpan.FromMilliseconds(300); var subscription = clicks .GroupByUntil( _ => 0, // 所有事件归入同一分组 group => group.Throttle(doubleClickTime) // 超时无新事件则关闭分组 ) .SelectMany(group => group.ToList()) .Subscribe(clickList => { if (clickList.Count > 1) { Debug.WriteLine("Double Click"); } else { Debug.WriteLine("Single Click"); } });
方法三:Timeout+Amb组合判断
分别定义单击和双击的事件流,再合并去重:
var doubleClickTime = TimeSpan.FromMilliseconds(300); // 单击流:点击后超时无后续点击则触发 var singleClickStream = clicks .SelectMany(click => Observable.Return(click) .Timeout(doubleClickTime, Observable.Empty<Click>()) ) .Select(_ => "Single Click"); // 双击流:连续两次点击后触发 var doubleClickStream = clicks .Zip(clicks.Skip(1), (first, second) => new[] { first, second }) .Throttle(doubleClickTime) .Select(_ => "Double Click"); // 合并流并去重,避免重复触发 var clickOperationStream = Observable.Merge(singleClickStream, doubleClickStream) .DistinctUntilChanged(); clickOperationStream.Subscribe(result => Debug.WriteLine(result));
内容的提问来源于stack exchange,提问作者James B
相关产品推荐
相关产品推荐

