作商城類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; }
效果圖以下: