xamarin.forms之實現ListView列表倒計時

   作商城類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;
        }

效果圖以下:

相關文章
相關標籤/搜索