Refactoring UI
This commit is contained in:
390
MainPage.xaml.cs
390
MainPage.xaml.cs
@ -8,318 +8,224 @@ namespace HeartRateMonitorAndroid;
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
private const string Tag = "HeartRateMonitor";
|
||||
private const int GraphUpdateIntervalMs = 1000; // 每秒更新一次图表
|
||||
private const string DefaultWebsocketUrl = "wss://ws.nuanr-mxi.com/ws"; // 默认WebSocket服务器地址
|
||||
private const int UIUpdateIntervalMs = 1000; // 每秒更新一次UI
|
||||
|
||||
// 图表更新定时器
|
||||
private IDispatcherTimer _graphUpdateTimer;
|
||||
// UI更新定时器
|
||||
private IDispatcherTimer _uiUpdateTimer;
|
||||
|
||||
// WebSocket相关
|
||||
private WebSocketService.HeartRateWebSocketClient _webSocketClient;
|
||||
private bool _isWebSocketEnabled = false;
|
||||
private string _webSocketUrl = DefaultWebsocketUrl;
|
||||
// 数据<EFBFBD><EFBFBD>务
|
||||
private readonly IHeartRateDataService _dataService;
|
||||
|
||||
// 后台模式标志
|
||||
private bool _isRunningInBackground = false;
|
||||
|
||||
// 服务
|
||||
private readonly BluetoothService _bluetoothService;
|
||||
|
||||
// 心率数据模型
|
||||
private readonly HeartRateSessionData _sessionData = new();
|
||||
// 心率数据图表
|
||||
private readonly HeartRateGraphDrawable _heartRateGraph = new();
|
||||
|
||||
// 会话开始时间
|
||||
private DateTime _sessionStartTime = DateTime.Now;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 初始化蓝牙服务
|
||||
_bluetoothService = new BluetoothService();
|
||||
_bluetoothService.StatusUpdated += UpdateStatus;
|
||||
_bluetoothService.HeartRateUpdated += UpdateHeartRate;
|
||||
_bluetoothService.DeviceDiscovered += OnHeartRateDeviceDiscovered;
|
||||
// 获取数据服务实例
|
||||
_dataService = HeartRateDataService.Instance;
|
||||
|
||||
// 初始化图表
|
||||
heartRateGraphicsView.Drawable = _heartRateGraph;
|
||||
|
||||
// 初始化图表更新定时器
|
||||
InitializeGraphUpdateTimer();
|
||||
// 订阅服务事件
|
||||
SubscribeToServiceEvents();
|
||||
|
||||
// 检查蓝牙状态
|
||||
_bluetoothService.CheckBluetoothState();
|
||||
// 初始化UI更新定时器
|
||||
InitializeUITimer();
|
||||
|
||||
// 启动后台服务
|
||||
_ = StartBackgroundServiceAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化图表更新定时器
|
||||
/// 订阅服务事件
|
||||
/// </summary>
|
||||
private void InitializeGraphUpdateTimer()
|
||||
private void SubscribeToServiceEvents()
|
||||
{
|
||||
_graphUpdateTimer = Dispatcher.CreateTimer();
|
||||
_graphUpdateTimer.Interval = TimeSpan.FromMilliseconds(GraphUpdateIntervalMs);
|
||||
_graphUpdateTimer.Tick += async (s, e) => await UpdateGraph();
|
||||
_graphUpdateTimer.Start();
|
||||
_dataService.HeartRateDataReceived += OnHeartRateDataReceived;
|
||||
_dataService.ServiceStatusChanged += OnServiceStatusChanged;
|
||||
_dataService.DeviceStatusChanged += OnDeviceStatusChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新图表和通知
|
||||
/// 初始化UI更新定时器
|
||||
/// </summary>
|
||||
private async Task UpdateGraph()
|
||||
private void InitializeUITimer()
|
||||
{
|
||||
// 发送心率数据到服务器
|
||||
await SendHeartRateToServerAsync(_sessionData.LatestHeartRate);
|
||||
_uiUpdateTimer = Dispatcher.CreateTimer();
|
||||
_uiUpdateTimer.Interval = TimeSpan.FromMilliseconds(UIUpdateIntervalMs);
|
||||
_uiUpdateTimer.Tick += async (s, e) => await UpdateUI();
|
||||
_uiUpdateTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动后台服务
|
||||
/// </summary>
|
||||
private async Task StartBackgroundServiceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateServiceStatusUI("正在启动后台服务...", false);
|
||||
await _dataService.StartBackgroundServiceAsync();
|
||||
Debug.WriteLine($"{Tag}: 后台服务启动请求已发送");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 启动后台服务失败: {ex.Message}");
|
||||
UpdateServiceStatusUI("服务启动失败", false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新UI界面
|
||||
/// </summary>
|
||||
private async Task UpdateUI()
|
||||
{
|
||||
var sessionData = _dataService.GetCurrentSessionData();
|
||||
|
||||
// 更新图表
|
||||
_heartRateGraph.UpdateData(_sessionData.HeartRateData);
|
||||
heartRateGraphicsView.Invalidate();
|
||||
|
||||
// 重置新数据标记
|
||||
_sessionData.ResetNewDataFlag();
|
||||
|
||||
// 如果在后台运行且有数据,更新通知
|
||||
if (_isRunningInBackground && _sessionData.HeartRateData.Count > 0)
|
||||
if (sessionData.HasNewHeartRateData)
|
||||
{
|
||||
var duration = _sessionData.GetSessionDuration();
|
||||
NotificationService.ShowHeartRateNotification(
|
||||
_sessionData.LatestHeartRate,
|
||||
_sessionData.AverageHeartRate,
|
||||
_sessionData.MinHeartRate,
|
||||
_sessionData.MaxHeartRate,
|
||||
duration);
|
||||
_heartRateGraph.UpdateData(sessionData.HeartRateData);
|
||||
heartRateGraphicsView.Invalidate();
|
||||
sessionData.ResetNewDataFlag();
|
||||
}
|
||||
|
||||
// 更新会话时长
|
||||
var duration = DateTime.Now - _sessionStartTime;
|
||||
durationLabel.Text = $"{duration:hh\\:mm\\:ss}";
|
||||
|
||||
// 更新数据点数
|
||||
dataPointsLabel.Text = sessionData.HeartRateData.Count.ToString();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态标签
|
||||
/// 心率数据接收事件处理
|
||||
/// </summary>
|
||||
private void UpdateStatus(string status)
|
||||
private void OnHeartRateDataReceived(int heartRate)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
statusLabel.Text = status;
|
||||
try
|
||||
{
|
||||
var sessionData = _dataService.GetCurrentSessionData();
|
||||
|
||||
// 更新当前心率显示
|
||||
heartRateLabel.Text = $"{heartRate} bpm";
|
||||
lastUpdateLabel.Text = $"更新时间: {DateTime.Now:HH:mm:ss}";
|
||||
|
||||
// 隐藏无数据提示
|
||||
if (sessionData.HeartRateData.Count >= 1)
|
||||
{
|
||||
noDataLayout.IsVisible = false;
|
||||
}
|
||||
|
||||
// 更新统计信息
|
||||
minHeartRateLabel.Text = sessionData.MinHeartRate.ToString();
|
||||
maxHeartRateLabel.Text = sessionData.MaxHeartRate.ToString();
|
||||
avgHeartRateLabel.Text = sessionData.AverageHeartRate.ToString("0");
|
||||
|
||||
//Debug.WriteLine($"{Tag}: UI已更新心率数据: {heartRate} bpm");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 更新心率UI失败: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新心率数据
|
||||
/// 服务状态变化事件处理
|
||||
/// </summary>
|
||||
private void UpdateHeartRate(int heartRate)
|
||||
private void OnServiceStatusChanged(ServiceStatus status)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
// 更新UI上的心率显示
|
||||
heartRateLabel.Text = $"心率: {heartRate} BPM";
|
||||
|
||||
// 添加新的心率数据点
|
||||
_sessionData.AddHeartRate(heartRate);
|
||||
|
||||
// 如果是第一个数据点,隐藏提示标签
|
||||
if (_sessionData.HeartRateData.Count == 1)
|
||||
try
|
||||
{
|
||||
noDataLabel.IsVisible = false;
|
||||
UpdateServiceStatusUI(status.StatusMessage, status.IsRunning);
|
||||
UpdateUploadStatusUI(status.IsWebSocketConnected ? "已连接" : "未连接", status.IsWebSocketConnected);
|
||||
|
||||
Debug.WriteLine($"{Tag}: 服务状态已更新: {status.StatusMessage}");
|
||||
}
|
||||
|
||||
// 更新统计信息显示
|
||||
minHeartRateLabel.Text = _sessionData.MinHeartRate.ToString();
|
||||
maxHeartRateLabel.Text = _sessionData.MaxHeartRate.ToString();
|
||||
avgHeartRateLabel.Text = _sessionData.AverageHeartRate.ToString("0");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扫描按钮点击事件
|
||||
/// </summary>
|
||||
private async void OnScanClicked(object sender, EventArgs e)
|
||||
{
|
||||
await _bluetoothService.StartScanAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 心率设备发现事件处理
|
||||
/// </summary>
|
||||
private async void OnHeartRateDeviceDiscovered(Plugin.BLE.Abstractions.Contracts.IDevice device)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(async () => {
|
||||
statusLabel.Text = $"检测到心率设备: {device.Name ?? "未知设备"}";
|
||||
await _bluetoothService.ConnectToDeviceAsync(device);
|
||||
|
||||
// 连接成功后重置心率数据显示
|
||||
if (_bluetoothService.ConnectedDevice != null)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_sessionData.ResetData();
|
||||
minHeartRateLabel.Text = "--";
|
||||
maxHeartRateLabel.Text = "--";
|
||||
avgHeartRateLabel.Text = "--";
|
||||
noDataLabel.IsVisible = true;
|
||||
heartRateGraphicsView.Invalidate();
|
||||
Debug.WriteLine($"{Tag}: 更新服务状态UI失败: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后台运行按钮点击事件
|
||||
/// 设备状态变化事件处理
|
||||
/// </summary>
|
||||
private async void OnBackgroundClicked(object sender, EventArgs e)
|
||||
private void OnDeviceStatusChanged(DeviceConnectionStatus status)
|
||||
{
|
||||
if (_bluetoothService.ConnectedDevice == null)
|
||||
{
|
||||
await DisplayAlert("提示", "请先连接心率设备", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunningInBackground = !_isRunningInBackground;
|
||||
|
||||
if (_isRunningInBackground)
|
||||
{
|
||||
backgroundButton.Text = "停止后台运行";
|
||||
|
||||
// 显示通知
|
||||
if (_sessionData.HeartRateData.Count > 0)
|
||||
MainThread.BeginInvokeOnMainThread(() => {
|
||||
try
|
||||
{
|
||||
TimeSpan duration = _sessionData.GetSessionDuration();
|
||||
NotificationService.ShowHeartRateNotification(
|
||||
_sessionData.LatestHeartRate,
|
||||
_sessionData.AverageHeartRate,
|
||||
_sessionData.MinHeartRate,
|
||||
_sessionData.MaxHeartRate,
|
||||
duration);
|
||||
connectionStatusLabel.Text = status.ConnectionMessage;
|
||||
|
||||
// 如果设备重新连接,重置会话开始时间
|
||||
if (status.IsConnected && !string.IsNullOrEmpty(status.DeviceName))
|
||||
{
|
||||
_sessionStartTime = DateTime.Now;
|
||||
|
||||
// 重置UI<55><49>示
|
||||
minHeartRateLabel.Text = "--";
|
||||
maxHeartRateLabel.Text = "--";
|
||||
avgHeartRateLabel.Text = "--";
|
||||
noDataLayout.IsVisible = true;
|
||||
heartRateGraphicsView.Invalidate();
|
||||
}
|
||||
|
||||
Debug.WriteLine($"{Tag}: 设备状态已更新: {status.ConnectionMessage}");
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
NotificationService.ShowHeartRateNotification(0, 0, 0, 0, TimeSpan.Zero);
|
||||
Debug.WriteLine($"{Tag}: 更新设<E696B0><E8AEBE>状态UI失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 通知用户应用将在后台运行
|
||||
await DisplayAlert("后台运行", "应用将在后台继续监测心率。可以通过通知栏查看实时数据。", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundButton.Text = "后台运行";
|
||||
|
||||
// 取消通知
|
||||
Services.NotificationService.CancelNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket设置按钮点击事件
|
||||
/// 更新服务状态UI
|
||||
/// </summary>
|
||||
private async void OnWebSocketSettingsClicked(object sender, EventArgs e)
|
||||
private void UpdateServiceStatusUI(string status, bool isRunning)
|
||||
{
|
||||
string result = await DisplayPromptAsync(
|
||||
"配置数据上传",
|
||||
"请输入WebSocket服务器地址:\n格式:wss://example.com/ws",
|
||||
"确定",
|
||||
"取消",
|
||||
_webSocketUrl,
|
||||
maxLength: 100,
|
||||
keyboard: Keyboard.Url);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(result)) return;
|
||||
|
||||
if (!result.StartsWith("ws://") && !result.StartsWith("wss://"))
|
||||
{
|
||||
// fallback到默认websocket服务器
|
||||
result = _webSocketUrl;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 更新按钮状态,显示正在连接
|
||||
webSocketButton.Text = "正在连接...";
|
||||
webSocketButton.IsEnabled = false;
|
||||
|
||||
// 初始化WebSocket客户端
|
||||
await InitializeWebSocketClientAsync(result);
|
||||
|
||||
if (_isWebSocketEnabled)
|
||||
{
|
||||
webSocketButton.Text = "数据上传已启用";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#28A745"); // 绿色
|
||||
await DisplayAlert("连接成功", "心率数据将会实时上传到服务器", "确定");
|
||||
// 禁用按钮
|
||||
webSocketButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
webSocketButton.Text = "配置数据上传";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#6C757D"); // 灰色
|
||||
await DisplayAlert("连接失败", "无法连接到指定的WebSocket服务器,请检查地址或网络连接", "确定");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
webSocketButton.Text = "配置数据上传";
|
||||
webSocketButton.BackgroundColor = Color.FromArgb("#6C757D"); // 灰色
|
||||
await DisplayAlert("错误", $"配置WebSocket时出错: {ex.Message}", "确定");
|
||||
}
|
||||
finally
|
||||
{
|
||||
webSocketButton.IsEnabled = true;
|
||||
}
|
||||
serviceStatusLabel.Text = status;
|
||||
statusIndicator.Fill = isRunning ? Color.FromArgb("#28A745") : Color.FromArgb("#DC3545");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化WebSocket客户端
|
||||
/// 更新数据上传状态UI
|
||||
/// </summary>
|
||||
private async Task InitializeWebSocketClientAsync(string url = null)
|
||||
private void UpdateUploadStatusUI(string status, bool isConnected)
|
||||
{
|
||||
// 释放现有的WebSocket客户端
|
||||
if (_webSocketClient != null)
|
||||
{
|
||||
_webSocketClient.Dispose();
|
||||
_webSocketClient = null;
|
||||
}
|
||||
|
||||
// 使用提供的URL或默认URL
|
||||
_webSocketUrl = string.IsNullOrEmpty(url) ? DefaultWebsocketUrl : url;
|
||||
|
||||
try
|
||||
{
|
||||
_webSocketClient = new Services.WebSocketService.HeartRateWebSocketClient(_webSocketUrl);
|
||||
await _webSocketClient.ConnectAsync();
|
||||
_isWebSocketEnabled = true;
|
||||
Debug.WriteLine($"{Tag}: WebSocket客户端已初始化,连接到 {_webSocketUrl}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"{Tag}: 初始化WebSocket客户端失败: {ex.Message}");
|
||||
_isWebSocketEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送心率数据到WebSocket服务器
|
||||
/// </summary>
|
||||
private async Task SendHeartRateToServerAsync(int heartRate)
|
||||
{
|
||||
if (!_isWebSocketEnabled || _webSocketClient == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var data = new WebSocketService.HeartRateData
|
||||
{
|
||||
HeartRate = heartRate,
|
||||
Timestamp = DateTime.Now,
|
||||
DeviceName = _bluetoothService.ConnectedDevice?.Name ?? "未知设备"
|
||||
};
|
||||
|
||||
await _webSocketClient.SendHeartRateDataAsync(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"发送心率数据失败: {ex.Message}");
|
||||
}
|
||||
uploadStatusLabel.Text = status;
|
||||
uploadStatusLabel.TextColor = isConnected ? Color.FromArgb("#28A745") : Color.FromArgb("#DC3545");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 页面卸载时清理资源
|
||||
/// </summary>
|
||||
~MainPage()
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
if (_webSocketClient != null)
|
||||
{
|
||||
_webSocketClient.Dispose();
|
||||
_webSocketClient = null;
|
||||
}
|
||||
base.OnDisappearing();
|
||||
|
||||
// 停止定时器
|
||||
_uiUpdateTimer?.Stop();
|
||||
|
||||
_bluetoothService.Dispose();
|
||||
// 取消订阅事件
|
||||
if (_dataService != null)
|
||||
{
|
||||
_dataService.HeartRateDataReceived -= OnHeartRateDataReceived;
|
||||
_dataService.ServiceStatusChanged -= OnServiceStatusChanged;
|
||||
_dataService.DeviceStatusChanged -= OnDeviceStatusChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user