xamarin.forms之实现ListView列表倒计时

释放双眼,带上耳机,听听看~!

   做商城类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;
        }

效果图如下:

 

人已赞赏
随笔日记

vue-router导航守卫(router.beforeEach())的使用

2020-11-9 4:28:06

随笔日记

Asp.Net Core&钉钉开发系列

2020-11-9 4:28:08

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索