有一个带有答案的问题,显示了如何创建在指定时间段内运行的进度条。这是该问题的链接:

How can I create a bar area that slowly fills from left to right over 5, 10 or ?? seconds?

我已经对此进行了测试,并且效果很好。但是,我想知道如何扩展它,以便可以在完成之前取消/停止进度条,然后再次重新启动。

问题和答案非常受欢迎,因此似乎这可能会使许多人受益。

我将不胜感激关于可能的方法的任何想法和反馈。

更新 1:

我尝试实现该解决方案,但出现错误,希望得到一些建议。我正在使用您的所有新代码,并在此处从旧代码更改为新代码:

           <local:TimerView x:Name="timerView">
                <local:TimerView.ProgressBar>
                    <BoxView BackgroundColor="Maroon" />
                </local:TimerView.ProgressBar>
                <local:TimerView.TrackBar>
                    <BoxView BackgroundColor="Gray" />
                </local:TimerView.TrackBar>
            </local:TimerView>
            <!--<Grid x:Name="a">
                <local:TimerView x:Name="timerView1" VerticalOptions="FillAndExpand">
                    <local:TimerView.ProgressBar>
                        <Frame HasShadow="false" Padding="0" Margin="0" BackgroundColor="#AAAAAA" CornerRadius="0" VerticalOptions="FillAndExpand" />
                    </local:TimerView.ProgressBar>
                    <local:TimerView.TrackBar>
                        <Frame HasShadow="false" Padding="0" Margin="0" CornerRadius="0" BackgroundColor="#EEEEEE" VerticalOptions="FillAndExpand" />
                    </local:TimerView.TrackBar>
                </local:TimerView>
            </Grid>
            <Grid x:Name="b">
                <local:TimerView x:Name="timerView2" VerticalOptions="FillAndExpand">
                    <local:TimerView.ProgressBar>
                        <Frame HasShadow="false" Padding="0" Margin="0" BackgroundColor="#AAAAAA" CornerRadius="0" VerticalOptions="FillAndExpand" />
                    </local:TimerView.ProgressBar>
                    <local:TimerView.TrackBar>
                        <Frame HasShadow="false" Padding="0" Margin="0" CornerRadius="0" BackgroundColor="#EEEEEE" VerticalOptions="FillAndExpand" />
                    </local:TimerView.TrackBar>
                </local:TimerView>
            </Grid>-->

三问

首先 - 我注意到你把 timerView 分成两个文件。属性文件似乎以某种方式链接到主文件。从图形上看,属性文件从 timerView 缩进。你如何在 Visual Studio 中进行这种链接?我刚刚创建了两个文件,这有什么不同。

第二个 - 当我尝试编译代码时,出现此错误:

/Users//Documents/Phone app/Japanese7/Japanese/Views/Phrases/PhrasesFrame.xaml(10,10):错误:位置 117:10。缺少公共(public)静态 GetProgressBar 或附加属性“Japanese.TimerView.ProgressBarProperty”的公共(public)实例属性 getter(日语)

你有什么想法可能导致这种情况吗?一切看起来都和以前一样。

第三个 - 我注意到你使用 BoxView 而我使用了一个框架。代码可以使用吗?

更新 2:

在我的后端 C# 代码中,我使用以下内容来启动计时器:
timerView.StartTimerCommand
     .Execute(TimeSpan.FromSeconds(App.pti.Val()));

我试图用一些类似的语法停止计时器,但有一些问题。您能否让我知道在您的解决方案中使用 C# 后端而不是 MVVM 时如何停止计时器:
timerView.StopTimerCommand.Execute(); // Give syntax error

最佳答案

第 1 步:ViewExtensions 中添加取消方法:

public static class ViewExtensions
{
    static string WIDTH_ANIMATION_NAME = "WidthTo";
    public static Task<bool> WidthTo(this VisualElement self, double toWidth, uint length = 250, Easing easing = null)
    {
         ...
    }

    public static void CancelWidthToAnimation(this VisualElement self)
    {
        if(self.AnimationIsRunning(WIDTH_ANIMATION_NAME))
            self.AbortAnimation(WIDTH_ANIMATION_NAME);
    }
}

第 2 步: 为“暂停”和“停止”/“取消”命令添加可绑定(bind)属性;和一个属性来跟踪计时器是否正在运行。
public static readonly BindableProperty PauseTimerCommandProperty =
    BindableProperty.Create(
        "PauseTimerCommand", typeof(ICommand), typeof(TimerView),
        defaultBindingMode: BindingMode.OneWayToSource,
        defaultValue: default(ICommand));

public ICommand PauseTimerCommand
{
    get { return (ICommand)GetValue(PauseTimerCommandProperty); }
    set { SetValue(PauseTimerCommandProperty, value); }
}

public static readonly BindableProperty StopTimerCommandProperty =
    BindableProperty.Create(
        "StopTimerCommand", typeof(ICommand), typeof(TimerView),
        defaultBindingMode: BindingMode.OneWayToSource,
        defaultValue: default(ICommand));

public ICommand StopTimerCommand
{
    get { return (ICommand)GetValue(StopTimerCommandProperty); }
        set { SetValue(StopTimerCommandProperty, value); }
}

public static readonly BindableProperty IsTimerRunningProperty =
    BindableProperty.Create(
        "IsTimerRunning", typeof(bool), typeof(TimerView),
        defaultBindingMode: BindingMode.OneWayToSource,
        defaultValue: default(bool), propertyChanged: OnIsTimerRunningChanged);

public bool IsTimerRunning
{
    get { return (bool)GetValue(IsTimerRunningProperty); }
    set { SetValue(IsTimerRunningProperty, value); }
}

private static void OnIsTimerRunningChanged(BindableObject bindable, object oldValue, object newValue)
{
    ((TimerView)bindable).OnIsTimerRunningChangedImpl((bool)oldValue, (bool)newValue);
}

第 3 步: 更新 TimerView 如下以使用 StopWatch 来跟踪时间、暂停和取消。
public partial class TimerView : AbsoluteLayout
{
    readonly Stopwatch _stopWatch = new Stopwatch();
    public TimerView()
    {
        ...
    }

    async void HandleStartTimerCommand(object param = null)
    {
        if (IsTimerRunning)
            return;

        ParseForTime(param);
        if (InitRemainingTime())
            _stopWatch.Reset();

        SetProgressBarWidth();

        IsTimerRunning = true;

        //Start animation
        await ProgressBar.WidthTo(0, Convert.ToUInt32(RemainingTime.TotalMilliseconds));

        //reset state
        IsTimerRunning = false;
    }

    void HandlePauseTimerCommand(object unused)
    {
        if (!IsTimerRunning)
            return;

        ProgressBar.CancelWidthToAnimation(); //abort animation
    }

    void HandleStopTimerCommand(object unused)
    {
        if (!IsTimerRunning)
            return;

        ProgressBar.CancelWidthToAnimation(); //abort animation

        ResetTimer(); //and reset timer
    }

    protected virtual void OnIsTimerRunningChangedImpl(bool oldValue, bool newValue)
    {
        if (IsTimerRunning)
        {
            _stopWatch.Start();
            StartIntervalTimer(); //to update RemainingTime
        }
        else
            _stopWatch.Stop();

        ((Command)StartTimerCommand).ChangeCanExecute();
        ((Command)PauseTimerCommand).ChangeCanExecute();
        ((Command)StopTimerCommand).ChangeCanExecute();
    }

    bool _intervalTimer;
    void StartIntervalTimer()
    {
        if (_intervalTimer)
            return;

        Device.StartTimer(TimeSpan.FromMilliseconds(100), () =>
        {
            if(IsTimerRunning)
            {
                var remainingTime = Time.TotalMilliseconds - _stopWatch.Elapsed.TotalMilliseconds;
                if (remainingTime <= 100)
                {
                    _intervalTimer = false;
                    ResetTimer();
                }
                else
                    RemainingTime = TimeSpan.FromMilliseconds(remainingTime);
            }

            return _intervalTimer = IsTimerRunning; //stop device-timer if timer was stopped
        });
    }

    private void ResetTimer()
    {
        ProgressBar.CancelWidthToAnimation();
        RemainingTime = default(TimeSpan); //reset timer
        SetProgressBarWidth(); //reset width
    }

    void SetProgressBarWidth()
    {
        if (RemainingTime == Time)
            SetLayoutBounds(ProgressBar, new Rectangle(0, 0, Width, Height));
        else
        {
            var progress = ((double)RemainingTime.Seconds / Time.Seconds);
            SetLayoutBounds(ProgressBar, new Rectangle(0, 0, Width * progress, Height));
        }
    }
    ...
}

示例用法
<controls:TimerView x:Name="timerView">
    <controls:TimerView.ProgressBar>
        <BoxView BackgroundColor="Maroon" />
    </controls:TimerView.ProgressBar>
    <controls:TimerView.TrackBar>
        <BoxView BackgroundColor="Gray" />
    </controls:TimerView.TrackBar>
</controls:TimerView>

<Label Text="{Binding Path=RemainingTime, StringFormat='{0:%s}:{0:%f}', Source={x:Reference timerView}}" />

<Button Command="{Binding StartTimerCommand, Source={x:Reference timerView}}" Text="Start Timer">
    <Button.CommandParameter>
        <x:TimeSpan>0:0:20</x:TimeSpan>
    </Button.CommandParameter>
</Button>

<Button Command="{Binding PauseTimerCommand, Source={x:Reference timerView}}" Text="Pause Timer" />

<Button Command="{Binding StopTimerCommand, Source={x:Reference timerView}}" Text="Stop Timer" />

xamarin - 如何在 Xamarin 中停止和启动定时进度条?-LMLPHP

TimerBarSample 上传的工作样本

编辑 1

首先 - 这真的没有什么区别 - 你甚至可以将所有代码合并到一个文件中。可以使用 <DependentOn /> 标记实现缩进链接 - 类似于用于 XAML 文件的代码隐藏 cs 的标记。

第二个 - 我已将 protected 访问修饰符添加到可绑定(bind)属性的 getter 或 setter。但是看起来在应用 XAMLC 时它失败了。我已经更新了 github 示例中的代码。

第三个 - 是的,任何继承自 View 的控件(无论是 BoxView 还是 Frame )都可以使用。

编辑 2

由于这些命令(可绑定(bind)属性)属于 ICommand 类型,为了 Execute - 您需要传入一个参数。如果命令不需要参数 - 您可以使用 null

推荐用法:
 if(timerView.StopTimerCommand.CanExecute(null))
      timerView.StopTimerCommand.Execute(null);

关于xamarin - 如何在 Xamarin 中停止和启动定时进度条?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46382755/

10-12 17:19
查看更多