有一个带有答案的问题,显示了如何创建在指定时间段内运行的进度条。这是该问题的链接:
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" />
在 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/