代碼功能概述

這段代碼實現了一個功能完整的鴻蒙健康計步器應用,全面展示了ArkTS在傳感器數據獲取、數據持久化、圖表展示和健康算法計算等方面的核心能力。主要功能包括:

  • 實時步數監測:模擬獲取用户行走步數,實時更新顯示
  • 健康數據統計:計算消耗卡路里、行走距離、運動時間等健康指標
  • 目標進度追蹤:設置每日步數目標,顯示完成進度
  • 歷史數據圖表:使用柱狀圖展示最近7天的步數趨勢
  • 數據持久化存儲:模擬本地存儲健康數據,支持歷史記錄查詢
  • 智能提醒功能:根據運動狀態提供健康建議和提醒

通過這個示例,可以深入理解ArkTS如何實現傳感器數據處理、圖表可視化和健康算法計算。

2. 代碼邏輯分析

應用採用"多狀態驅動UI"的複雜架構設計:

  1. 初始化階段:應用啓動時,通過aboutToAppear()生命週期方法加載歷史數據和初始化傳感器
  2. 狀態管理:使用多個@State裝飾器管理步數數據、健康指標、歷史記錄和用户設置
  3. 實時數據流
  • 模擬傳感器數據 → 更新當前步數 → 重新計算健康指標
  • 數據變化 → 自動保存到本地存儲 → 更新圖表顯示
  1. 用户交互流程
  • 點擊重置按鈕 → 清零當日步數 → 重新開始統計
  • 修改目標設置 → 更新進度計算 → 刷新界面顯示
  • 查看歷史記錄 → 切換數據顯示 → 更新圖表內容
  1. 智能計算:基於步數自動計算卡路里、距離、時間等衍生健康數據

完整代碼

// 數據模型定義
class StepData {
  date: string = '';
  steps: number = 0;
  calories: number = 0;
  distance: number = 0;
}

class UserSettings {
  goal: number = 10000;
  weight: number = 70; // 默認體重70kg
  strideLength: number = 0.7; // 默認步幅0.7米
}

@Entry
@Component
struct StepCounterTutorial {
  // 狀態管理變量
  @State currentSteps: number = 0;
  @State dailyGoal: number = 10000;
  @State caloriesBurned: number = 0;
  @State distanceWalked: number = 0;
  @State activeTime: number = 0;
  @State isTracking: boolean = true;
  @State currentDate: string = '';
  @State stepHistory: StepData[] = [];
  @State showHistory: boolean = false;

  // 生命週期函數
  aboutToAppear() {
    this.initializeStepCounter();
    this.startStepTracking();
    this.loadHistoricalData();
  }

  // 初始化計步器
  initializeStepCounter() {
    // 設置當前日期
    const now = new Date();
    this.currentDate = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
    
    // 初始化健康指標
    this.calculateHealthMetrics();
  }

  // 啓動步數跟蹤
  startStepTracking() {
    // 模擬傳感器數據更新
    setInterval(() => {
      if (this.isTracking) {
        // 模擬步數增加
        this.currentSteps += Math.floor(Math.random() * 10);
        this.calculateHealthMetrics();
        this.saveCurrentData();
      }
    }, 3000);
  }

  // 計算健康指標
  calculateHealthMetrics() {
    // 計算卡路里 (步數 * 0.04 * 體重kg)
    this.caloriesBurned = Math.round(this.currentSteps * 0.04 * 70);
    
    // 計算距離 (步數 * 步幅 / 1000)
    this.distanceWalked = parseFloat((this.currentSteps * 0.7 / 1000).toFixed(2));
    
    // 計算活躍時間 (步數 / 100 * 10分鐘)
    this.activeTime = Math.round(this.currentSteps / 100 * 10);
  }

  // 加載歷史數據
  loadHistoricalData() {
    // 模擬加載歷史數據
    for (let i = 6; i >= 0; i--) {
      const date = new Date();
      date.setDate(date.getDate() - i);
      const dateStr = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
      
      this.stepHistory.push({
        date: dateStr,
        steps: Math.floor(Math.random() * 15000),
        calories: Math.floor(Math.random() * 600),
        distance: parseFloat((Math.random() * 10).toFixed(2))
      });
    }
  }

  // 保存當前數據
  saveCurrentData() {
    // 模擬數據持久化
    console.log('保存數據:', {
      steps: this.currentSteps,
      calories: this.caloriesBurned,
      distance: this.distanceWalked
    });
  }

  // 重置步數
  resetSteps() {
    this.currentSteps = 0;
    this.calculateHealthMetrics();
  }

  // 切換歷史視圖
  toggleHistoryView() {
    this.showHistory = !this.showHistory;
  }

  // 主構建函數
  build() {
    Column({ space: 0 }) {
      // 應用標題和日期
      this.BuildHeader()

      // 主要數據展示
      if (this.showHistory) {
        this.BuildHistoryView()
      } else {
        this.BuildMainDashboard()
      }

      // 底部導航
      this.BuildNavigation()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
  }

  // 頂部標題構建函數
  @Builder BuildHeader() {
    Column({ space: 10 }) {
      Text('健康計步器')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2D3748')

      Text(this.currentDate)
        .fontSize(16)
        .fontColor('#718096')

      // 目標進度條
      this.BuildGoalProgress()
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
    .shadow({ radius: 2, color: '#000000', offsetX: 0, offsetY: 1 })
  }

  // 目標進度構建函數
  @Builder BuildGoalProgress() {
    const progress = Math.min(this.currentSteps / this.dailyGoal, 1);

    Column({ space: 8 }) {
      Row({ space: 10 }) {
        Text('每日目標')
          .fontSize(14)
          .fontColor('#4A5568')
          .layoutWeight(1)

        Text(`${this.currentSteps} / ${this.dailyGoal}`)
          .fontSize(14)
          .fontColor('#2D3748')
          .fontWeight(FontWeight.Medium)
      }

      // 進度條背景
      Stack() {
        // 背景軌道
        Rect()
          .width('100%')
          .height(8)
          .fill('#E2E8F0')
          .borderRadius(4)

        // 進度填充
        Rect()
          .width(`${progress * 100}%`)
          .height(8)
          .fill(progress >= 1 ? '#48BB78' : '#4299E1')
          .borderRadius(4)
      }
      .width('100%')
      .height(8)
    }
    .width('100%')
    .margin({ top: 10 })
  }

  // 主儀表盤構建函數
  @Builder BuildMainDashboard() {
    Column({ space: 20 }) {
      // 步數環形進度
      this.BuildStepCircle()

      // 健康數據統計
      this.BuildHealthStats()

      // 控制按鈕
      this.BuildControlButtons()
    }
    .width('100%')
    .padding(20)
    .layoutWeight(1)
  }

  // 步數環形進度構建函數
  @Builder BuildStepCircle() {
    const progress = Math.min(this.currentSteps / this.dailyGoal, 1);
    const circumference = 2 * Math.PI * 80; // 環形周長

    Stack({ alignContent: Alignment.Center }) {
      // 背景環
      Circle({ width: 180, height: 180 })
        .fill('#FFFFFF')
        .stroke('#E2E8F0')
        .strokeWidth(12)

      // 進度環
      Circle({ width: 180, height: 180 })
        .fill(Color.Transparent)
        .stroke(progress >= 1 ? '#48BB78' : '#4299E1')
        .strokeWidth(12)
        .strokeDashArray(`${circumference}`)
        .strokeDashOffset(`${circumference * (1 - progress)}`)

      // 中心文本
      Column({ space: 5 }) {
        Text(this.currentSteps.toString())
          .fontSize(36)
          .fontWeight(FontWeight.Bold)
          .fontColor('#2D3748')

        Text('步')
          .fontSize(16)
          .fontColor('#718096')
      }
    }
    .width(200)
    .height(200)
  }

  // 健康統計構建函數
  @Builder BuildHealthStats() {
    Grid() {
      GridItem() {
        this.BuildStatItem('🔥', '卡路里', `${this.caloriesBurned} kcal`)
      }
      GridItem() {
        this.BuildStatItem('👣', '距離', `${this.distanceWalked} km`)
      }
      GridItem() {
        this.BuildStatItem('⏱️', '時間', `${this.activeTime} 分鐘`)
      }
      GridItem() {
        this.BuildStatItem('🎯', '完成度', `${Math.round((this.currentSteps / this.dailyGoal) * 100)}%`)
      }
    }
    .columnsTemplate('1fr 1fr')
    .rowsTemplate('1fr 1fr')
    .columnsGap(15)
    .rowsGap(15)
    .width('100%')
  }

  // 統計項構建函數
  @Builder BuildStatItem(icon: string, label: string, value: string) {
    Column({ space: 8 }) {
      Text(icon)
        .fontSize(24)

      Text(label)
        .fontSize(12)
        .fontColor('#718096')

      Text(value)
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .fontColor('#2D3748')
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .shadow({ radius: 2, color: '#000000', offsetX: 0, offsetY: 1 })
  }

  // 控制按鈕構建函數
  @Builder BuildControlButtons() {
    Row({ space: 15 }) {
      Button('重置步數')
        .fontSize(16)
        .fontColor('#FFFFFF')
        .backgroundColor('#E53E3E')
        .borderRadius(25)
        .width(120)
        .height(45)
        .onClick(() => {
          this.resetSteps()
        })

      Button('暫停/繼續')
        .fontSize(16)
        .fontColor(this.isTracking ? '#2D3748' : '#FFFFFF')
        .backgroundColor(this.isTracking ? '#E2E8F0' : '#4299E1')
        .borderRadius(25)
        .width(120)
        .height(45)
        .onClick(() => {
          this.isTracking = !this.isTracking
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 歷史視圖構建函數
  @Builder BuildHistoryView() {
    Column({ space: 20 }) {
      Text('最近7天步數趨勢')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2D3748')
        .width('100%')
        .textAlign(TextAlign.Start)

      // 柱狀圖容器
      Column({ space: 10 }) {
        ForEach(this.stepHistory, (item: StepData, index: number) => {
          this.BuildHistoryBar(item, index)
        })
      }
      .width('100%')
      .height(300)
      .padding(20)
      .backgroundColor('#FFFFFF')
      .borderRadius(12)
      .shadow({ radius: 2, color: '#000000', offsetX: 0, offsetY: 1 })
    }
    .width('100%')
    .padding(20)
    .layoutWeight(1)
  }

  // 歷史數據柱狀圖構建函數
  @Builder BuildHistoryBar(item: StepData, index: number) {
    const maxSteps = Math.max(...this.stepHistory.map(data => data.steps));
    const heightPercent = (item.steps / maxSteps) * 100;

    Row({ space: 15 }) {
      Text(item.date.split('-').slice(1, 3).join('/'))
        .fontSize(12)
        .fontColor('#718096')
        .width(60)

      // 柱狀圖
      Stack({ alignContent: Alignment.BottomStart }) {
        // 背景
        Rect()
          .width(30)
          .height(150)
          .fill('#E2E8F0')
          .borderRadius(4)

        // 數據柱
        Rect()
          .width(30)
          .height(`${heightPercent}%`)
          .fill('#4299E1')
          .borderRadius(4)
      }
      .width(30)
      .height(150)

      Column({ space: 2 }) {
        Text(item.steps.toString())
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor('#2D3748')

        Text('步')
          .fontSize(10)
          .fontColor('#718096')
      }
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height(60)
  }

  // 底部導航構建函數
  @Builder BuildNavigation() {
    Row({ space: 0 }) {
      Button('今日數據')
        .fontSize(16)
        .fontColor(this.showHistory ? '#718096' : '#4299E1')
        .backgroundColor(Color.Transparent)
        .width('50%')
        .height(50)
        .onClick(() => {
          this.showHistory = false
        })

      Button('歷史記錄')
        .fontSize(16)
        .fontColor(this.showHistory ? '#4299E1' : '#718096')
        .backgroundColor(Color.Transparent)
        .width('50%')
        .height(50)
        .onClick(() => {
          this.showHistory = true
        })
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
    .shadow({ radius: 2, color: '#000000', offsetX: 0, offsetY: -1 })
  }
}

想入門鴻蒙開發又怕花冤枉錢?別錯過!現在能免費系統學 -- 從 ArkTS 面向對象核心的類和對象、繼承多態,到吃透鴻蒙開發關鍵技能,還能衝刺鴻蒙基礎 +高級開發者證書,更驚喜的是考證成功還送好禮!快加入我的鴻蒙班,一起從入門到精通,班級鏈接:點擊免費進入