contentloadingprogressbar - ¿Cómo puedo detener e iniciar una barra de progreso cronometrada en Xamarin?
contentloadingprogressbar xamarin (1)
Hay una pregunta con una respuesta que muestra cómo se puede crear una barra de progreso que se ejecuta durante un período de tiempo específico. Aquí hay un enlace a esa pregunta:
¿Cómo puedo crear un área de barra que se llena lentamente de izquierda a derecha sobre 5, 10 o? ¿segundos?
Lo he probado y funciona bien. Sin embargo, me gustaría saber cómo puedo ampliar esto para que la barra de progreso pueda cancelarse / detenerse antes de completarse y luego reiniciarse nuevamente.
La pregunta y la respuesta fueron muy populares, por lo que parece que esto es algo que podría beneficiar a muchas personas.
Agradecería cualquier idea y comentarios sobre posibles formas en que esto podría hacerse.
Actualización 1:
Traté de implementar la solución pero me aparece un error y agradecería algunos consejos. Estoy usando todo tu código nuevo y cambio de antiguo a nuevo aquí:
<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>-->
Tres preguntas
Primero , noté que dividiste timerView en dos archivos. El archivo de propiedades parece estar relacionado de algún modo con el archivo principal. Gráficamente, el archivo de propiedades aparece sangrado de timerView. ¿Cómo haces esto enlazando en Visual Studio? Acabo de crear dos archivos, ¿eso marca la diferencia?
Segundo : cuando intento compilar el código, obtengo este error:
/ Usuarios // Documentos / Aplicación de teléfono / Japanese7 / Japanese / Views / Phrases / PhrasesFrame.xaml (10,10): Error: posición 117: 10. Falta un getProgressBar estático público o un getter de propiedad de instancia pública para la propiedad adjunta "Japanese.TimerView.ProgressBarProperty" (japonés)
¿Tienes alguna idea de lo que podría estar causando esto? Todo se ve igual que antes.
En tercer lugar , me doy cuenta de que usas BoxView y utilicé un Frame. ¿Funcionaría el código con cualquiera de los dos?
Actualización 2:
En mi código backend C # uso lo siguiente para iniciar el temporizador:
timerView.StartTimerCommand
.Execute(TimeSpan.FromSeconds(App.pti.Val()));
Traté de detener el temporizador con una sintaxis similar, pero hay algún problema. ¿Me puede decir cómo puedo detener el temporizador cuando se usa con el back-end C # en lugar de MVVM en su solución?
timerView.StopTimerCommand.Execute(); // Give syntax error
Paso 1: agregue el método de cancelar a 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);
}
}
Paso 2: agrega propiedades enlazables para los comandos ''pausa'' y ''detener'' / ''cancelar''; y una propiedad para rastrear si el temporizador se está ejecutando.
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);
}
Paso 3: Actualice TimerView
como se TimerView
continuación para usar un StopWatch
para rastrear el tiempo, pausar y cancelar.
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));
}
}
...
}
Uso de la muestra
<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" />
Muestra de trabajo cargada en TimerBarSample
EDIT 1
Primero - Realmente no hace la diferencia - incluso puede fusionar todo el código en un archivo. La vinculación con sangría se puede lograr usando la etiqueta <DependentOn />
, similar a la que se usa para código-detrás de cs
para archivos XAML.
Segundo : agregué modificadores de acceso protected
a getters o setters de propiedades enlazables. Pero parece que falla cuando se aplica XAMLC. He actualizado el código en la muestra de github.
Tercero : Sí, se puede BoxView
cualquier control que herede de View
(ya sea BoxView
o Frame
).
EDIT 2
Como estos comandos (propiedades enlazables) son de tipo ICommand
, para Execute
, debe pasar un parámetro. En caso de que el comando no necesite un parámetro, puede usar null
.
Uso recomendado:
if(timerView.StopTimerCommand.CanExecute(null))
timerView.StopTimerCommand.Execute(null);