【问题标题】:Reactive Framework / DoubleClick反应式框架/DoubleClick
【发布时间】:2011-03-08 04:30:06
【问题描述】:

我知道有一个简单的方法可以做到这一点 - 但今晚它打败了我......

我想知道两个事件是否在 300 毫秒内发生,例如双击。

在 300 毫秒内单击鼠标左键两次 - 我知道这是构建反应式框架的目的 - 但该死的,如果我能找到一个很好的文档,其中包含所有扩展运算符的简单示例 - Throttle、BufferWithCount、BufferWithTime - 所有只是没有'为我做这件事......

【问题讨论】:

    标签: system.reactive reactive-programming


    【解决方案1】:

    TimeInterval 方法将为您提供值之间的时间。

    public static IObservable<Unit> DoubleClicks<TSource>(
        this IObservable<TSource> source, TimeSpan doubleClickSpeed, IScheduler scheduler)
    {
        return source
            .TimeInterval(scheduler)
            .Skip(1)
            .Where(interval => interval.Interval <= doubleClickSpeed)
            .RemoveTimeInterval();
    }
    

    如果你想确保三次点击不会触发值,你可以只在一个热的 observable 上使用Repeat(我在这里使用了FastSubject,因为点击都会出现在一个线程上,因此不需要普通科目的沉重):

    public static IObservable<TSource> DoubleClicks<TSource>(
        this IObservable<TSource> source, TimeSpan doubleClickSpeed, IScheduler scheduler)
    {
        return source.Multicast<TSource, TSource, TSource>(
            () => new FastSubject<TSource>(), // events won't be multithreaded
            values =>
            {
                return values
                    .TimeInterval(scheduler)
                    .Skip(1)
                    .Where(interval => interval.Interval <= doubleClickSpeed)
                    .RemoveTimeInterval()
                    .Take(1)
                    .Repeat();
            });
    }
    

    【讨论】:

    • 非常感谢,我从我认为非常简单的练习中学到了很多东西。哪里有关于所有这些不同扩展的完整文档???你是怎么学的?我唯一的问题是关于重复,因为我对观察者何时超出范围感到困惑(害羞不取消订阅)?如果我在我的类的构造函数中订阅,范围是如何建立的?我知道 OnComplete 调用了 dispose,但是监听事件何时真正完成?
    • @codputer - 我的大部分知识来自于我在将框架移植到 actionscript 时针对每个运算符编写单元测试的事实。我已经记录了大多数操作员,尽管它们现在有点过时了(而且我知道 Throttle 文档是错误的) - github.com/richardszalay/rxas/wiki/Operators
    • @codputer - 观察者永远不会超出范围,除非相关控件超出范围,直到控件超出范围(因为订阅控件的事件将观察者与对象图中的控件联系起来) .在您处置订阅之前,不会删除事件订阅。或者,您可以使用 TakeUntil 或其他终止订阅的运算符自动取消订阅。
    • @codputer - 当您想按照到达顺序将两个流中的值配对时,您可以使用Zip。 Scott 的示例是使用它将Timestampd 流与自身(跳过一个的第一个值)组合,以便选择两个时间值之间的差异。在这种情况下根本没有必要,因为有一个内置的 TimeInterval 运算符。
    【解决方案2】:

    编辑 - 改用 TimeInterval()

    Zip() and Timestamp() 运算符可能是一个好的开始。

    var ioClicks = Observable.FromEvent<MouseButtonEventHandler, RoutedEventArgs>(
                                            h => new MouseButtonEventHandler(h),
                                            h => btn.MouseLeftButtonDown += h,
                                            h => btn.MouseLeftButtonDown -= h);
    var ioTSClicks = ioClicks.Timestamp();
    
    var iodblClicks = ioTSClicks.Zip(ioTSClicks.Skip(1), 
                                     (r, l) => l.Timestamp - r.Timestamp)
                                .Where(tspan => tspan.TotalMilliseconds < 300);
    

    最好通过测试调度程序对此进行测试,这样你就知道你得到了什么:

    [Fact]
    public void DblClick()
    {
        // setup
        var ioClicks = _scheduler.CreateHotObservable(
                         OnNext(210, "click"),
                         OnNext(220, "click"),
                         OnNext(300, "click"),
                         OnNext(365, "click"))
                         .Timestamp(_scheduler);
    
        // act
        Func<IObservable<TimeSpan>> target = 
            () => ioClicks.Zip(ioClicks.Skip(1), 
                              (r, l) => l.Timestamp - r.Timestamp)
                            .Where(tspan => tspan.Ticks < 30);
        var actuals = _scheduler.Run(target);
    
        // assert
        Assert.Equal(actuals.Count(), 1);
        // + more
    }
    public static Recorded<Notification<T>> OnNext<T>(long ticks, T value)
    {
        return new Recorded<Notification<T>>(
            ticks, 
            new Notification<T>.OnNext(value));
    }
    

    【讨论】:

    • 也谢谢你——我差点把你的答案称为你的答案,但我认为 FastSubject 很有趣,可以确保所有事件都发生在同一个线程上。不过做了+1!我很好奇你是从哪里了解到反应式框架的,以及你所有各种扩展的文档来源。
    • @codputer - 我想你误会了。我的示例使用FastSubject因为不需要线程处理(即鼠标点击总是来自 UI 线程)。这是防止来自多个线程的值的正常主题实现。
    猜你喜欢
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 2019-06-13
    • 2016-02-13
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多