如何使用Rx迭代一系列具有暂停和淡入淡出的字符串?

时间:2016-10-26 12:33:21

标签: c# xaml mvvm system.reactive

我的要求是从一个数组开始,从第一个开始,然后在5秒之后移动到下一个字符串,同时淡出并在Xamarin中使用Rx和XAML淡入下一个字符串。您可以假设这是在具有“消息”的视图模型上进行的。财产和消息容量'适用于文本和0到1之间的小数的属性。您还可以假设我有一个后台调度程序和UiScheduler设置。

我对Rx来说相当新,这将是显而易见的,到目前为止已经达到了这个目标:

var messages = new[] { 
                       "Welcome", 
                       "We are settings things up for you", 
                       "This may take a little while first time" };

Observable.Interval(TimeSpan.FromSeconds(5), Scheduler.BackgroundScheduler)
                  .SelectMany((long arg) => messages)
                  .Buffer(1, 1)
                  .SubscribeOn(Scheduler.UiScheduler)
                  .Subscribe((obj) => 
        {
            Message = obj[0];
        });    

上述情况并不奏效,因为缓冲区并不像我预期的那样工作。相反,它每5秒快速连续发射4个字符串,而不是逐步遍历每个字符串。

我不明白的是如何按照每个' x秒来顺序浏览每个字符串。在一个正确的' Rx'时尚和(作为我的奖励!)如何随后触发另一个可观察的每条新消息,以便在每次更改时将不透明度从0增加到1。

目的是实现“窗户10”和“窗户10”。样式屏幕,用于发生大更新或用户等待长时间操作完成时。

2 个答案:

答案 0 :(得分:3)

你可以使用Zip运算符和Observable.Interval来提供你想要的迭代字符串输出:

[Fact]
public void ShouldIterateThroughStringsEveryFiveSeconds()
{
    TestScheduler scheduler = new TestScheduler();

    string[] messages = new[]
    {
                "Welcome",
                "We are settings things up for you",
                "This may take a little while first time"
    };

    var expected = new[]
    {
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(0).Ticks, "Welcome"),
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(5).Ticks, "We are settings things up for you"),
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(10).Ticks, "This may take a little while first time"),
        ReactiveTest.OnCompleted<string>(ReactiveTest.Subscribed + TimeSpan.FromSeconds(15).Ticks)
    };

    var actual = scheduler.Start(
        // Solution
        () => Observable.Zip(
            messages.ToObservable(),
            Observable.Interval(TimeSpan.FromSeconds(5), scheduler).StartWith(0),
            (text, time) => text),
        TimeSpan.FromSeconds(20).Ticks
    );

    Assert.Equal(expected, actual.Messages.ToArray());
}

编辑:您可以将它们组合起来,而不是第二个可观察的不透明度:

[Fact]
public void ShouldIterateThroughStringsEveryFiveSecondsProvidingStringAndOpacity()
{
    TestScheduler scheduler = new TestScheduler();

    string[] messages = new[]
    {
                "Welcome",
                "We are settings things up for you",
                "This may take a little while first time"
    };

    var expected = new[]
    {
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(0).Ticks, Tuple.Create("Welcome", 0.0)),
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(5).Ticks, Tuple.Create("We are settings things up for you", 0.5)),
        ReactiveTest.OnNext(ReactiveTest.Subscribed + TimeSpan.FromSeconds(10).Ticks, Tuple.Create("This may take a little while first time", 1.0)),
        ReactiveTest.OnCompleted<Tuple<string, double>>(ReactiveTest.Subscribed + TimeSpan.FromSeconds(15).Ticks)
    };

    var actual = scheduler.Start(
        // Solution
        () => Observable
            .Zip(
                messages.ToObservable(), 
                Observable.Interval(TimeSpan.FromSeconds(5), scheduler).StartWith(0),
                (text, time) => text)
            .Select((text, index) => Tuple.Create(text, Convert.ToDouble(index) / Convert.ToDouble(messages.Length - 1))),
        TimeSpan.FromSeconds(20).Ticks
    );

    Assert.Equal(expected, actual.Messages.ToArray());
}

请注意,第一个元素的不透明度将为零,因此您将看不到它。您可能希望稍微更改数学(提供偏移量)以将不透明度从非零值缩放到1。

希望有所帮助:0)

答案 1 :(得分:2)

我会使用Observable.Generate

Observable
    .Generate(
        1,
        x => x < messages.Length,
        x => x + 1,
        x => x,
        x => TimeSpan.FromSeconds(5.0))
    .StartWith(0)
    .Select(x => messages[x])
    .Subscribe((obj) => 
    {
        Message = obj[0];
    });   

您可以尝试使用此版本,以确保在处理开始之前复制源数组 - 这可以避免可能的副作用。

Observable
    .Create<string>(o =>
    {
        var ms = messages.ToArray();
        return Observable
            .Generate(
                1,
                x => x < ms.Length,
                x => x + 1,
                x => x,
                x => TimeSpan.FromSeconds(5.0))
            .StartWith(0)
            .Select(x => ms[x])
            .Subscribe(o);
    })
    .Subscribe((obj) =>
    {
        Message = obj[0];
    });