作商城类APP时常常会遇到抢购倒计时的功能,以前作小区宝iOS的时候也有相似的功能,想着参考iOS作的思路,自定义一个Cell,在Cell中每秒刷新一下控件的文本值,但使用xamarin.forms实现时,自定义cell的方式并不可行,小伙伴上周给发了一个倒计时功能的demo:https://github.com/jsuarezruiz/MyTripCountdown,demo是以下图实现的是一个时间的倒计时效果,须要将一个倒计时的功能放在列表中,实现多个倒计时的效果, 看了源码也一直没思路,昨天也是没思路报着试一试的心态动手操做了下,没想到成功了,仍是很是有成就感的。git
1、定义计时器github
xamarin.forms提供了Device.StartTimer来实现定时任务,每隔一秒须要触发事件改变剩余时间。这里定义了两个Action,Completed是在倒计时结束时触发,Ticked是每秒触发一次。RemainTime是剩余时间timespan,EndDate为结束时间。ide
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; }); } } }
2、设置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引用改变属性的值。ui
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; } } }
3、设置ViewModelspa
新建继承BaseViewModel的类CountDownViewModel,在CountDownViewModel中定义了倒计时类CountDown,当CountDownViewModel调用构造函数时实例化倒计时CountDown,EndDate经过时间戳得到,以后调用LoadAsync()方法,启动计时器,并为计时器绑定具体Actio,在Ticked的Action中每秒定时刷新绑定到界面的数值。code
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(); } } }
4、测试orm
在MainPage中设置了一个ListView,ViewCell模板中设置了一个Label,Text值绑定了CountDownTitle。在MainPage的构造方法中设置listview的ItemsSource。xml
<?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; }
效果图以下: