如何基于TimeSpan变量计算到当前时间的已流逝时长
定时器持久化实现方案
核心思路
每个ListView条目绑定独立的持久化标识和计时数据,避免多条目定时器互相干扰,沿用你现有DateTime计算耗时的逻辑即可实现需求。
实现步骤
- 定义条目持久化字段
每个条目需要独立存储3个核心数据:
- 条目唯一ID(用于区分不同条目的持久化键)
- 条目启动时间戳(DateTime类型,持久化存储)
- 条目总时长(就是你现有
TimeSheet.TotalTime的整数值,和条目绑定存储)
- 单条目启动计时逻辑
// 假设当前点击的待启动条目为selectedItem,包含Id、TotalTime属性 // 构造唯一持久化键,避免和其他条目冲突 var startAtKey = $"Timer_Start_{selectedItem.Id}"; var startAt = DateTime.Now; // 持久化当前条目的启动时间 Application.Current.Properties[startAtKey] = startAt; // 你原有的总时长转换逻辑保留 var totalDuration = TimeSpan.FromMinutes(selectedItem.TotalTime); // 绑定取消令牌到当前条目,方便后续手动停止 var cts = new CancellationTokenSource(); selectedItem.TimerCts = cts; Device.StartTimer(TimeSpan.FromSeconds(1), () => { if (cts.IsCancellationRequested) { return false; } // 计算已流逝时长,和你参考示例逻辑完全一致 var elapsedTime = DateTime.Now - startAt; // 可选:判断是否达到总时长,自动停止 if (elapsedTime >= totalDuration) { cts.Cancel(); // 持久化删除已完成的计时数据 Application.Current.Properties.Remove(startAtKey); Device.BeginInvokeOnMainThread(() => { selectedItem.TxtTimer = "计时完成"; }); return false; } // 更新当前条目的计时显示 Device.BeginInvokeOnMainThread(() => { selectedItem.TxtTimer = elapsedTime.ToString(@"hh\:mm\:ss"); }); return true; });
- 应用重启后恢复计时逻辑
如果需要实现杀应用后重启仍能接续计时,在页面初始化时遍历所有状态为「计时中」的条目,读取持久化的启动时间重新启动定时器即可:
// 遍历所有计时中的条目 foreach (var runningItem in ListViewDataSource.Where(x => x.IsTiming)) { var startAtKey = $"Timer_Start_{runningItem.Id}"; if (Application.Current.Properties.TryGetValue(startAtKey, out var savedStartObj) && savedStartObj is DateTime savedStartAt) { var totalDuration = TimeSpan.FromMinutes(runningItem.TotalTime); var cts = new CancellationTokenSource(); runningItem.TimerCts = cts; // 重启定时器,流逝时长直接用当前时间减历史启动时间,自动补全中间离线的时长 Device.StartTimer(TimeSpan.FromSeconds(1), () => { if (cts.IsCancellationRequested) return false; var elapsedTime = DateTime.Now - savedStartAt; if (elapsedTime >= totalDuration) { cts.Cancel(); Application.Current.Properties.Remove(startAtKey); Device.BeginInvokeOnMainThread(() => runningItem.TxtTimer = "计时完成"); return false; } Device.BeginInvokeOnMainThread(() => runningItem.TxtTimer = elapsedTime.ToString(@"hh\:mm\:ss")); return true; }); } }
- 手动停止计时处理
用户手动停止某条目标计时时,执行以下逻辑即可:
// 假设当前要停止的条目为stopItem stopItem.TimerCts?.Cancel(); // 删除持久化的启动时间 Application.Current.Properties.Remove($"Timer_Start_{stopItem.Id}"); stopItem.TxtTimer = "00:00:00";
注意:如果遇到
Application.Current.Properties存储DateTime出现序列化异常,可以把启动时间转成Unix时间戳(long类型)存储,读取时再转回DateTime,兼容性更好。
内容的提问来源于stack exchange,提问作者PHULUSO GOVERN RAMULIFHO
相关产品推荐
相关产品推荐

