做商城类 App 时经常会遇到抢购倒计时的功能, 之前做小区宝 iOS 的时候也有类似的功能, 想着参考 iOS 做的思路, 自定义一个 Cell, 在 Cell 中每秒刷新一下控件的文本值, 但使用 xamarin.forms 实现时, 自定义 cell 的方式并不可行, 小伙伴上周给发了一个倒计时功能的 demo:https://github.com/jsuarezruiz/MyTripCountdown,demo 是如下图实现的是一个时间的倒计时效果, 需要将一个倒计时的功能放在列表中, 实现多个倒计时的效果, 看了源码也一直没思路, 昨天也是没思路报着试一试的心态动手操作了下, 没想到成功了, 还是非常有成就感的.
一, 定义计时器
xamarin.forms 提供了 Device.StartTimer 来实现定时任务, 每隔一秒需要触发事件改变剩余时间. 这里定义了两个 Action,Completed 是在倒计时结束时触发, Ticked 是每秒触发一次. RemainTime 是剩余时间 timespan,EndDate 为结束时间.
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Xamarin.Forms;
- namespace TimeCountDown
- {
- public class CountDown : BindableObject
- {
- TimeSpan _remainTime;
- public event Action Completed;
- public event Action Ticked;
- public DateTime EndDate { get; set; }
- public TimeSpan RemainTime
- {
- get { return _remainTime; }
- private set
- {
- _remainTime = value;
- OnPropertyChanged();
- }
- }
- public void Start(int seconds = 1)
- {
- Device.StartTimer(TimeSpan.FromSeconds(seconds), () =>
- {
- RemainTime = (EndDate - DateTime.Now);
- var ticked = RemainTime.TotalSeconds> 1;
- if (ticked)
- {
- Ticked?.Invoke();
- }
- else
- {
- Completed?.Invoke();
- }
- return ticked;
- });
- }
- }
- }
二, 设置 BaseViewModel
这里创建了一个 BaseViewModel, 并有 2 个方法, LoadAsync(),UnloadAsync(), 而且继承了 ExtendedBindableObject.
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading.Tasks;
- namespace TimeCountDown
- {
- public abstract class BaseViewModel : ExtendedBindableObject
- {
- public virtual Task LoadAsync()
- {
- return Task.CompletedTask;
- }
- public virtual Task UnloadAsync()
- {
- return Task.CompletedTask;
- }
- }
- }
在 ExtendedBindableObject 中扩展了 BindableObject, 增加了 SetProperty 方法, SetProperty 方法使用 ref 引用改变属性的值.
- using System;
- using System.Collections.Generic;
- using System.Runtime.CompilerServices;
- using System.Text;
- using Xamarin.Forms;
- namespace TimeCountDown
- {
- public class ExtendedBindableObject : BindableObject
- {
- protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "")
- {
- if (EqualityComparer<T>.Default.Equals(backingStore, value))
- {
- return false;
- }
- backingStore = value;
- OnPropertyChanged(propertyName);
- return true;
- }
- }
- }
三, 设置 ViewModel
新建继承 BaseViewModel 的类 CountDownViewModel, 在 CountDownViewModel 中定义了倒计时类 CountDown, 当 CountDownViewModel 调用构造函数时实例化倒计时 CountDown,EndDate 通过时间戳获得, 之后调用 LoadAsync() 方法, 启动计时器, 并为计时器绑定具体 Actio, 在 Ticked 的 Action 中每秒定时刷新绑定到界面的数值.
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading.Tasks;
- namespace TimeCountDown
- {
- public class CountDownViewModel : BaseViewModel
- {
- public long Tick { get; set; }
- private string _countDownTitle;
- public string CountDownTitle
- {
- get => _countDownTitle;
- set => SetProperty(ref _countDownTitle, value);
- }
- private CountDown _countDown;
- public CountDownViewModel(long ticks)
- {
- Tick = ticks;
- _countDown = new CountDown() { EndDate = DateTime.Now.Add(new TimeSpan(ticks)) };
- LoadAsync();
- }
- public override Task LoadAsync()
- {
- _countDown.Start();
- _countDown.Ticked += OnCountdownTicked;
- _countDown.Completed += OnCountdownCompleted;
- return base.LoadAsync();
- }
- public override Task UnloadAsync()
- {
- _countDown.Ticked -= OnCountdownTicked;
- _countDown.Completed -= OnCountdownCompleted;
- return base.UnloadAsync();
- }
- void OnCountdownTicked()
- {
- CountDownTitle = string.Format("{0}:{1}:{2} 后开抢", _countDown.RemainTime.Hours, _countDown.RemainTime.Minutes, _countDown.RemainTime.Seconds);
- }
- void OnCountdownCompleted()
- {
- CountDownTitle = "抢购进行中";
- UnloadAsync();
- }
- }
- }
四, 测试
在 MainPage 中设置了一个 ListView,ViewCell 模板中设置了一个 Label,Text 值绑定了 CountDownTitle. 在 MainPage 的构造方法中设置 listview 的 ItemsSource.
- <?xml version="1.0" encoding="utf-8" ?>
- <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
- xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
- xmlns:local="clr-namespace:TimeCountDown"
- x:Class="TimeCountDown.MainPage">
- <StackLayout>
- <ListView x:Name="listView" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
- <ListView.ItemTemplate>
- <DataTemplate>
- <ViewCell>
- <Label Text="{Binding CountDownTitle}" FontSize="14" TextColor="Black" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"></Label>
- </ViewCell>
- </DataTemplate>
- </ListView.ItemTemplate>
- </ListView>
- </StackLayout>
- </ContentPage>
- public MainPage()
- {
- InitializeComponent();
- List<CountDownViewModel> countDownVMs = new List<CountDownViewModel>() {
- new CountDownViewModel(11111111111),
- new CountDownViewModel(2222222222),
- new CountDownViewModel(3333333333333),
- new CountDownViewModel(444444444444),
- };
- listView.ItemsSource = countDownVMs;
- }
效果图如下:
来源: https://www.cnblogs.com/5ishare/p/10732899.html