鸿蒙实战案例-饮食记录-统计数据卡片(三)

前言


本文将介绍如何使用HarmonyOS的ArkTS语言编写一个健康app中的统计数据卡片功能。该功能主要包括日期选择、热量统计和营养素统计三个部分。通过直观的界面展示,帮助用户了解自己的热量和营养素摄入情况,从而更好地管理自己的饮食和运动。


一、饮食记录UI设计分析图例

二、各组件的详细及联系

一、StatsCard

首先定义了一个名为 `StatsCard` 的组件,其主要功能是在健康应用中展示统计数据卡片。用户可以通过日期选择对话框选择特定日期,系统默认为当前日期的起始时间。所选日期将用于分析和展示该日的热量和营养素摄入情况。通过 `CalorieStats` 组件展示用户在选定日期内的饮食摄入数据,并显示运动消耗的热量信息。通过 `NutrientStats` 组件展示用户在选定日期内的碳水化合物、蛋白质和脂肪等营养素的摄入量。

代码如下:
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import CalorieStats from './CalorieStats'
import DatePickDialog from './DatePickDialog'
import NutrientStats from './NutrientStats'
@Component
export default  struct StatsCard {
 
  @StorageProp('selectedDate') selectedDate:number = DateUtil.beginTimeOfDay(new Date())
 
  controller:CustomDialogController = new CustomDialogController({
    builder:DatePickDialog({selectedDate:new Date(this.selectedDate)})
  })
  build() {
    Column(){
      //1.日期信息
      Row(){
        Text(DateUtil.formatDate(this.selectedDate))
          .fontColor($r('app.color.secondary_color'))
        Image($r('app.media.ic_public_spinner'))
          .width(20)
          .fillColor($r('app.color.secondary_color'))
      }
      .padding(CommonConstants.SPACE_8)
      .onClick(()=>this.controller.open())
      //2.统计信息
      Swiper(){
        // 2.1.热量统计
        CalorieStats()
        // 2.2.营养素统计
        NutrientStats()
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(CommonConstants.DEFAULT_18)
    }
    .width(CommonConstants.THOUSANDTH_940)
    .backgroundColor($r('app.color.stats_title_bgc'))
    .borderRadius(CommonConstants.DEFAULT_18)
 
  }
}
上述代码运行截图:

二、DatePickDialog

`DatePickDialog` 组件是一个日期选择对话框,其主要功能是允许用户选择特定日期。该对话框使用 `DatePicker` 组件,其日期范围从 2020 年 1 月 1 日至当前日期。`DatePicker` 的 `start` 属性设置为 2020 年 1 月 1 日,`end` 属性设置为当前日期,`selected` 属性设置为 `this.selectedDate`,即默认选中当前日期。当用户在 `DatePicker` 中选择新日期时,会触发一个事件,通过该事件可以获取用户选择的年、月、日,并将这些值更新到 `this.selectedDate` 中。对话框底部提供取消按钮和确定按钮。点击取消按钮会关闭对话框,不保存任何更改;点击确定按钮会执行两个操作:首先,将选中的日期以毫秒形式保存到全局存储中,键名为 `selectedDate`;然后关闭对话框。

代码如下:
import { CommonConstants } from '../../common/constants/CommonConstants'
@Preview
@CustomDialog
export default struct DatePickDialog {
  controller:CustomDialogController
  selectedDate:Date = new Date()
  build() {
    Column({space:CommonConstants.SPACE_12}){
      //1.日期选择器
      DatePicker({
        start: new Date('2020-01-01'),
        end: new Date(),
        selected: this.selectedDate
      })
        .onChange((value: DatePickerResult) => {
          this.selectedDate.setFullYear(value.year, value.month, value.day)
        })
      //2.按钮
      Row({space:CommonConstants.SPACE_12}){
        Button('取消')
          .width(120)
          .backgroundColor($r('app.color.light_gray'))
          .onClick(()=>this.controller.close())
        Button('确定')
          .width(120)
          .backgroundColor($r('app.color.primary_color'))
          .onClick(()=>{
            //1.保存日期到全局存储
            AppStorage.SetOrCreate('selectedDate',this.selectedDate.getTime())
            //2.关闭窗口
            this.controller.close()
          })
      }
 
    }
    .padding(CommonConstants.SPACE_12)
  }
}

上述代码主要知识点:

  • 这段 TypeScript 代码定义了一个名为 `DatePickDialog` 的结构体组件,用于显示一个日期选择对话框。以下是关键知识点的总结:
  • 1. **导入语句**:
  •    - `import { CommonConstants } from '../../common/constants/CommonConstants'`:从常量文件中导入 `CommonConstants`,用于获取预定义的常量,例如间距值和颜色。
  • 2. **装饰器**:
  •    - `@Preview` 和 `@CustomDialog`:这些装饰器标注了 `DatePickDialog` 是一个预览组件和自定义对话框组件。这些装饰器可能来自于某个 UI 框架或库,用于定义组件的行为和外观。
  • 3. **成员变量**:
  •    - `controller: CustomDialogController`:用于控制对话框状态和行为的控制器。
  •    - `selectedDate: Date = new Date()`:存储用户选择的日期,默认为当前日期。
  • 4. **build 方法**:
  •    - `build()` 方法定义了对话框的内容和布局:
  •      - 使用 `Column` 组件创建垂直布局,设置间距为 `CommonConstants.SPACE_12`。
  •      - 包含两个主要部分:
  •        - **日期选择器** (`DatePicker`):允许用户选择日期,并将选中的日期更新到 `this.selectedDate`。
  •        - **按钮区域** (`Row`):水平布局的按钮区域,包括 "取消" 和 "确定" 两个按钮。
  •          - **取消按钮**:点击时调用控制器的 `close()` 方法,关闭对话框。
  •          - **确定按钮**:点击时将选中的日期以毫秒形式保存到全局存储中,并关闭对话框。
  • 5. **其他**: 
  • 、 - 使用常量 `CommonConstants.SPACE_12` 来设置布局元素之间的间距和内边距。
  •    - 使用 `$r('app.color.light_gray')` 和 `$r('app.color.primary_color')` 获取应用程序定义的颜色值,用于设置按钮的背景颜色。
运行截图:

三、CalorieStats

`CalorieStats` 组件用于展示用户的饮食摄入、运动消耗和推荐摄入量等信息。该组件包含一个名为 `remainCalorie` 的方法,用于计算剩余卡路里(即推荐摄入量减去饮食摄入加上运动消耗)。在 `build` 方法中,使用 `Row` 布局,将饮食摄入、剩余可摄入量和运动消耗的信息展示在同一行内。

在剩余可摄入量(即“还可以吃”按钮)的 `Stack` 中,使用 `Progress` 组件展示进度条,指示饮食摄入占推荐摄入量的比例。同时,通过 `StatsBuilder` 方法展示剩余可摄入量的卡路里数值及其推荐摄入量。此外,使用 `StatsBuilder` 方法分别展示饮食摄入和运动消耗的信息。

具体来说,`CalorieStats` 组件通过以下方式实现上述功能:

  • 1. **定义方法**:
  •    - `remainCalorie` 方法:计算剩余卡路里,其公式为推荐摄入量减去饮食摄入加上运动消耗。
  • 2. **布局设计**:
  •    - 在 `build` 方法中,使用 `Row` 布局将饮食摄入、剩余可摄入量和运动消耗的信息水平排列。
  •    - 剩余可摄入量部分采用 `Stack` 布局,其中包含一个 `Progress` 组件,用于显示饮食摄入占推荐摄入量的比例。
  • 3. **信息展示**:
  •    - 通过 `StatsBuilder` 方法,展示剩余可摄入量的详细信息,包括具体的卡路里数值和推荐摄入量。

   - 另外,使用 `StatsBuilder` 方法分别展示饮食摄入和运动消耗的具体数值,以提供用户全面的卡路里统计信息。

这种设计确保了 `CalorieStats` 组件能够以用户友好的方式展示关键的卡路里统计数据,并且通过合理的布局和进度条视觉化地表示摄入和消耗情况。

代码及其注释如下:
import { CommonConstants } from '../../common/constants/CommonConstants';

@Component
export default struct CalorieStats {
   // 初始化饮食摄入、运动消耗和推荐摄入量
   intake: number = 192; // 饮食摄入
   expend: number = 150; // 运动消耗
   recommend: number = CommonConstants.RECOMMEND_CALORIE; // 推荐摄入量

   // 计算剩余可摄入卡路里
   remainCalorie() {
      return this.recommend - this.intake + this.expend;
   }

   // 构建UI布局
   build() {
      Row({ space: CommonConstants.SPACE_6 }) {
         // 1. 饮食摄入
         this.StatsBuilder({ label: '饮食摄入', value: this.intake });

         // 2. 还可以吃
         Stack() {
            // 2.1. 进度条
            Progress({
               value: this.intake,
               total: this.recommend,
               type: ProgressType.Ring,
            })
               .width(120)
               .style({ strokeWidth: CommonConstants.DEFAULT_10 })
               .color($r('app.color.primary_color'));

            // 2.2. 统计数据
            this.StatsBuilder({
               label: '还可以吃',
               value: this.remainCalorie(),
               tips: `推荐${this.recommend}`,
            });
         }

         // 3. 运动消耗
         this.StatsBuilder({ label: '运动消耗', value: this.expend });
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .padding({ top: 30, bottom: 35 });
   }

   // 构建统计数据组件
   @Builder
   StatsBuilder($$: { label: string; value: number; tips?: string }) {
      Column({ space: CommonConstants.SPACE_6 }) {
         // 标签
         Text($$.label)
            .fontColor($r('app.color.gray'))
            .fontWeight(CommonConstants.FONT_WEIGHT_600);

         // 数值
         Text($$.value.toFixed(0))
            .fontSize(20)
            .fontWeight(CommonConstants.FONT_WEIGHT_700);

         // 提示信息(如果有)
         if ($$.tips) {
            Text($$.tips)
               .fontSize(12)
               .fontColor($r('app.color.light_gray'));
         }
      }
   }
}
这段代码主要涉及以下几个知识点:
  • 1. **组件定义与构建**:
  •    - 使用了 `@Component` 注解,表明 `CalorieStats` 是一个组件。
  •    - `CalorieStats` 结构体定义了三个属性 `intake`(摄入卡路里)、`expend`(消耗卡路里)、`recommend`(推荐摄入卡路里),并初始化了它们的默认值。
  • 2. **方法定义**:
  •    - `remainCalorie()` 方法计算剩余可摄入卡路里,根据推荐摄入量、摄入量和消耗量计算出剩余的卡路里数。
  • 3. **UI 构建**:
  •    - `build()` 方法用于构建用户界面(UI)。它使用了一系列 UI 组件和布局属性(如 `Row`、`Stack`、`Column`、`Text`、`Progress` 等),将数据以特定格式展示出来。
  •    - 在 `build()` 方法中,通过 `StatsBuilder` 方法构建了不同的统计数据项,并设置了相应的样式和布局。
  • 4. **常量和样式管理**:
  •    - 使用了 `CommonConstants` 中定义的常量,如 `RECOMMEND_CALORIE`、`SPACE_6`、`DEFAULT_10` 等,用于统一管理样式、间距等参数,提高代码的可维护性和可读性。
  • 5. **注释添加**:
  •    - 为了增强代码的可理解性,添加了详细的注释,解释了每个变量、方法和组件的作用和用途。

总体而言,这段代码展示了如何使用 TypeScript 和一个 UI 框架(假设是类似 Vue 或 React 的框架)来构建一个计算和展示卡路里摄入、消耗以及推荐摄入量的组件。通过结构化的方式管理数据和 UI,提高了代码的可维护性和复用性。

运行截图:

四、NutrientStats 

NutrientStats 组件用于展示用户摄入的营养成分(碳水化合物、蛋白质和脂肪)以及推荐的摄入量。定义了以下属性:

  • - `carbon`: 用户摄入的碳水化合物数量。
  • - `protein`: 用户摄入的蛋白质数量。
  • - `fat`: 用户摄入的脂肪数量。
  • - `recommendCarbon`: 推荐的碳水化合物摄入量。
  • - `recommendProtein`: 推荐的蛋白质摄入量。
  • - `recommendFat`: 推荐的脂肪摄入量。

`build()` 方法用于构建组件的布局。在该方法中,使用 `Row` 布局将三个营养成分(碳水化合物、蛋白质和脂肪)放置在一行,并设置了适当的间距。每个营养成分通过 `StatsBuilder` 实例进行展示,传入相应的标签、值、推荐值和颜色。

`StatsBuilder` 方法负责构建每个营养成分的展示部分。在该方法中,首先创建一个 `Column` 布局,然后在其中创建一个 `Stack` 布局,用于放置环形进度条和两个文本。环形进度条的值表示当前营养成分的摄入量,总值为推荐摄入量,类型为 `ProgressType.Ring`。进度条的宽度、线宽和颜色由参数指定。

接下来,创建两个文本标识:一个显示“摄入推荐”,另一个显示当前摄入量与推荐摄入量的比例。最后,创建一个文本显示营养成分的名称,并设置字体大小和颜色。整体布局的宽度、对齐方式和内边距也在 `build()` 方法中进行了设置。

代码如下:
import { CommonConstants } from '../../common/constants/CommonConstants';

// 定义 NutrientStats 组件,用于展示营养成分的摄入量和推荐量
@Component
export default struct NutrientStats {
  // 默认营养成分的摄入量
  carbon: number = 23;
  protein: number = 9;
  fat: number = 7;

  // 推荐的营养成分摄入量,使用常量定义
  recommendCarbon: number = CommonConstants.RECOMMEND_CARBON;
  recommendProtein: number = CommonConstants.RECOMMEND_PROTEIN;
  recommendFat: number = CommonConstants.RECOMMEND_FAT;

  // 构建方法,用于生成组件的布局
  build() {
    Row({ space: CommonConstants.SPACE_6 }) {
      // 调用 StatsBuilder 方法,分别展示碳水化合物、蛋白质和脂肪的营养成分情况
      this.StatsBuilder({
        label: '碳水化合物',
        value: this.carbon,
        recommend: this.recommendCarbon,
        color: $r('app.color.carbon_color') // 碳水化合物的颜色
      });
      this.StatsBuilder({
        label: '蛋白质',
        value: this.protein,
        recommend: this.recommendProtein,
        color: $r('app.color.protein_color') // 蛋白质的颜色
      });
      this.StatsBuilder({
        label: '脂肪',
        value: this.fat,
        recommend: this.recommendFat,
        color: $r('app.color.fat_color') // 脂肪的颜色
      });
    }
    .width('100%') // 设置整行的宽度为100%
    .justifyContent(FlexAlign.SpaceEvenly) // 水平分布排列元素
    .padding({ top: 30, bottom: 35 }); // 设置上下内边距
  }

  // StatsBuilder 方法,用于构建单个营养成分的展示部分
  @Builder StatsBuilder($$:{ label: string, value: number, recommend: number, color: ResourceStr }) {
    Column({ space: CommonConstants.SPACE_6 }) {
      Stack() {
        // 使用 Progress 组件展示环形进度条,显示当前摄入量与推荐摄入量
        Progress({
          value: $$.value,
          total: $$.recommend,
          type: ProgressType.Ring // 环形进度条类型
        })
        .width(95) // 进度条宽度
        .style({ strokeWidth: CommonConstants.DEFAULT_6 }) // 进度条线宽
        .color($$.color); // 进度条颜色

        Column({ space: CommonConstants.SPACE_6 }) {
          // 展示“摄入推荐”文本
          Text('摄入推荐')
            .fontSize(12)
            .fontColor($r('app.color.gray')); // 文本颜色

          // 显示当前摄入量和推荐摄入量的比例
          Text(`${$$.value.toFixed(0)}/${$$.recommend.toFixed(0)}`)
            .fontSize(18)
            .fontWeight(CommonConstants.FONT_WEIGHT_600); // 设置字体粗细
        }
      }

      // 显示营养成分名称和单位(克)
      Text(`${$$.label}(克)`)
        .fontSize(12)
        .fontColor($r('app.color.light_gray')); // 设置字体颜色
    }
  }
}
这段代码展示了一个典型的 Vue.js 组件的实现,主要涉及以下几个知识点和技术:
  • 1. **Vue 组件结构**:
  •    - 使用 `@Component` 装饰器定义组件,利用 `struct` 关键字创建结构体形式的组件。
  •    - 组件包含默认的营养成分摄入量(碳水化合物、蛋白质、脂肪)和推荐的营养成分摄入量。
  • 2. **布局和样式**:
  •    - 使用 `Row` 和 `Column` 组件来管理布局,以及分隔子组件之间的间距。
  •    - 使用 `Stack` 组件创建层叠效果,用于显示环形进度条和相关文本。
  • 3. **进度条和数据展示**:
  •    - 使用自定义的 `Progress` 组件展示环形进度条,用于比较当前摄入量与推荐摄入量。
  •    - 在进度条旁边显示当前摄入量和推荐摄入量的比例,以及相应的营养成分名称和单位。
  • 4. **样式和主题**:
  •    - 引用主题变量(如 `$r('app.color.carbon_color')`)来设置组件的颜色和样式,使得组件在不同主题下能够保持一致的外观。
  • 5. **方法和逻辑**:
  •    - 使用 `@Builder` 装饰器定义 `StatsBuilder` 方法,用于构建和组织单个营养成分的展示部分,提高代码的模块化和复用性。
  •    - 在 `build` 方法中调用 `StatsBuilder` 方法来动态生成多个营养成分的展示区域。

总体来说,这段代码展示了如何利用 Vue.js 的组件化思想和相关的生命周期方法、样式管理以及模块化开发,构建一个能够动态展示营养成分摄入情况的用户界面。

运行截图:

总结 

通过本文详细介绍了一个名为StatsCard的统计卡片组件的设计和实现细节,其中包括三个主要组件:DatePickDialog、CalorieStats和NutrientStats。

StatsCard作为主组件,实现了以下功能:

  • 1. **DatePickDialog组件**:用于选择日期,并在界面上显示用户所选的日期。
  • 2. **CalorieStats组件**:展示用户的饮食摄入、剩余可摄入以及运动消耗的热量统计数据。
  • 3. **NutrientStats组件**:展示用户摄入的碳水化合物、蛋白质和脂肪的详细统计数据。

每个组件通过统一的设计风格和模块化的开发方式,有效实现了功能模块的分离与复用,使得StatsCard能够在用户界面上动态展示并管理重要的健康数据统计信息。

  • 45
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值