HarmonyOS5 运动健康app(一):健康饮食(附代码)

一、核心数据模型设计

代码通过两个接口构建了饮食管理的基础数据结构:

interface footItem {name: string; // 营养名称(蛋白质/碳水/脂肪)weight: number; // 重量(克)
}interface DietItem {name: string; // 食物名称image: string; // 图片路径(如app.media.mantou)weight: number; // 单份重量(克)calorie: number; // 单份卡路里count: number; // 食用数量nengliang: string; // 主要营养素类型
}
  • 营养元素模型(footItem):将蛋白质、碳水、脂肪抽象为基础单元,用于统计每日摄入量。
  • 食物模型(DietItem):每个食物对象包含物理属性(重量、卡路里)和营养属性(nengliang),如:
{ name: '鸡蛋', image: 'app.media.egg', weight: 13, calorie: 200, count: 0, nengliang: '蛋白质' }

其中nengliang字段建立了食物与基础营养素的映射关系,为后续营养统计提供依据。

二、主组件架构:Index组件的状态与布局
@Entry
@Component
export struct Index {@State progressIndex: number = 0 // 总卡路里@State dbzIndex: number = 0 // 总蛋白质@State tsIndex: number = 0 // 总碳水@State zfIndex: number = 0 // 总脂肪// 头部统计组件@Buildertoubu() {Column({ space: 15 }) {// 环形卡路里进度条Stack() {Progress({ value: this.progressIndex, total: 20000, type: ProgressType.Ring }).width(90).height(90).style({ strokeWidth: 10 }).color('#4CD964').backgroundColor('#e0e0e0')Text(`${this.progressIndex} kcal`).fontSize(14).fontWeight(FontWeight.Bold)}// 营养素统计行Row() {ForEach(footData, (item) => {Column() {Text(this.getItemWeight(item).toString()).fontSize(18).fontColor('#333')Text(item.name).fontSize(14).fontColor('#666')}.width('30%')})}}.padding({ top: 20, bottom: 15 }).width('100%')}// 营养素与状态映射private getItemWeight(item: footItem): number {switch (item.name) {case '蛋白质': return this.dbzIndexcase '碳水': return this.tsIndexcase '脂肪': return this.zfIndexdefault: return 0}}build() {Column({ space: 15 }) {this.toubu()Text('饮食内容').fontSize(20).margin({ left: 20 })List() {ForEach(dietData, (item) => {ListItem() { foods({ item, progressIndex: this.progressIndex, ... }) }})}}.width('100%').padding({ left: 10, right: 10 })}
}
  • 状态管理:通过@State定义四大核心状态,分别追踪总卡路里和三类营养素摄入量,形成数据中枢。
  • 头部组件(toubu)
    • 环形进度条使用Progress组件,以20000kcal为目标值,绿色进度随progressIndex动态变化;
    • 营养素统计行通过ForEach遍历footData,将dbzIndex等状态映射为界面数值,实现"蛋白质:18g"等展示效果。

三、可复用组件:foods组件的交互逻辑
@Reusable
@Component
export struct foods {@State ifjiajian: boolean = false // 操作类型(增减)@Prop item: DietItem // 食物对象(只读)@Link progressIndex: number // 绑定总卡路里状态@Link dbzIndex: number // 绑定蛋白质状态(双向同步)// 卡路里计算calorieNUm() {const num = this.ifjiajian ? this.item.calorie * this.item.count : -this.item.calorie * (this.item.count + 1)this.progressIndex += num}// 营养素重量计算weightNUm() {const amount = this.ifjiajian ? this.item.count : -(this.item.count + 1)const weightChange = 13 * amountswitch (this.item.nengliang) {case '蛋白质': this.dbzIndex += weightChangecase '碳水': this.tsIndex += weightChangecase '脂肪': this.zfIndex += weightChange}}build() {Row() {Image($r(this.item.image)).width(60).height(60).borderRadius(8)Column() {Text(this.item.name).fontSize(16).fontWeight(FontWeight.Bold)Text(`${this.item.weight} 克`).fontSize(14).fontColor('#777')}.width('40%')Column() {Text(`${this.item.calorie * this.item.count} 卡`).fontSize(16)Row() {Text('-').onClick(() => {if (this.item.count > 0) {this.item.count--; this.ifjiajian = falsethis.calorieNUm(); this.weightNUm()}})Text(`${this.item.count}`).width(30).textAlign(TextAlign.Center)Text('+').onClick(() => {this.item.count++; this.ifjiajian = truethis.calorieNUm(); this.weightNUm()})}.width(90)}.width('40%')}.width('100%').padding({ left: 10, right: 10 })}
}
  • 状态绑定:通过@Link实现与主组件状态的双向同步,点击"+/-"按钮时,progressIndex和营养素状态会实时更新。
  • 交互逻辑
    • ifjiajian标记操作类型,增加时calorieNUm()计算正卡路里值,减少时计算负值;
    • weightNUm()根据nengliang属性(如"蛋白质")更新对应营养素总量,1份食物默认增加13克重量(与item.weight一致)。

四、数据流转与业务闭环
  1. 用户操作:点击食物卡片的"+"按钮 → item.count自增 → ifjiajian设为true
  2. 数据计算
    • calorieNUm()计算新增卡路里(如鸡蛋200卡×1份),累加到progressIndex
    • weightNUm()根据nengliang(蛋白质)计算13克重量,累加到dbzIndex
  1. 界面更新:主组件的环形进度条和营养素数值通过状态响应式机制自动刷新,形成"操作-计算-展示"的闭环。

五、附:代码
interface footItem {name: string; // 营养名称weight: number; // 重量
}interface DietItem {name: string; // 食物名称image: string; // 食物图片路径(本地或网络,这里用占位示意)weight: number; // 重量calorie: number; // 卡路里count: number; // 食用数量nengliang: string; // 营养名称(蛋白质、脂肪、碳水)
}const footData: footItem[] = [{ name: '蛋白质', weight: 0 },{ name: '碳水', weight: 0 },{ name: '脂肪', weight: 0 },
];const dietData: DietItem[] = [{ name: '馒头', image: 'app.media.mantou', weight: 13, calorie: 100, count: 0, nengliang: '蛋白质' },{ name: '油条', image: 'app.media.youtiao', weight: 13, calorie: 200, count: 0, nengliang: '脂肪' },{ name: '豆浆', image: 'app.media.doujiang', weight: 13, calorie: 300, count: 0, nengliang: '碳水' },{ name: '稀饭', image: 'app.media.xifan', weight: 13, calorie: 300, count: 0, nengliang: '碳水' },{ name: '鸡蛋', image: 'app.media.egg', weight: 13, calorie: 200, count: 0, nengliang: '蛋白质' },
];@Entry
@Component
export struct Index {@State progressIndex: number = 0 // 进度条进度(总大卡数)@State dbzIndex: number = 0 // 总蛋白质@State tsIndex: number = 0 // 总碳水@State zfIndex: number = 0 // 总脂肪// 头部组件@Buildertoubu() {Column({ space: 15 }) {Stack() {Progress({value: this.progressIndex,total: 20000,type: ProgressType.Ring}).width(90).height(90).style({ strokeWidth: 10 }).color('#4CD964').backgroundColor('#e0e0e0');Text(`${this.progressIndex} kcal`).fontSize(14).fontWeight(FontWeight.Bold).margin({ top: 5 })}Row() {ForEach(footData, (item: footItem) => {Column() {Text(this.getItemWeight(item).toString()).fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333')Text(item.name).fontSize(14).fontColor('#666')}.width('30%')}, (item: footItem) => JSON.stringify(item))}}.padding({ top: 20, bottom: 15 }).width('100%').alignItems(HorizontalAlign.Center)}// 获取对应的营养值private getItemWeight(item: footItem): number {switch (item.name) {case '蛋白质':return this.dbzIndex;case '碳水':return this.tsIndex;case '脂肪':return this.zfIndex;default:return 0;}}build() {Column({ space: 15 }) {this.toubu()Text('饮食内容').fontSize(20).fontColor('#555').width('100%').margin({ left: 20 })List({ space: 10 }) {ForEach(dietData, (item: DietItem) => {ListItem() {foods({item: item,progressIndex: this.progressIndex,dbzIndex: this.dbzIndex,tsIndex: this.tsIndex,zfIndex: this.zfIndex})}}, (item: DietItem) => JSON.stringify(item))}}.width('100%').padding({ left: 10,right: 10 })}
}// 饮食内容组件
@Reusable
@Component
export struct foods {@State ifjiajian: boolean = false@Prop item: DietItem@Link progressIndex: number@Link dbzIndex: number@Link tsIndex: number@Link zfIndex: number// 统计大卡数calorieNUm() {let num = this.ifjiajian ?this.item.calorie * this.item.count :-this.item.calorie * (this.item.count + 1);this.progressIndex += num;}// 统计能量weightNUm() {const amount = this.ifjiajian ? this.item.count : -(this.item.count + 1);const weightChange = 13 * amount;switch (this.item.nengliang) {case '蛋白质':this.dbzIndex += weightChange;break;case '碳水':this.tsIndex += weightChange;break;case '脂肪':this.zfIndex += weightChange;break;}}build() {Row() {Image($r(this.item.image)).width(60).height(60).borderRadius(8)Column({ space: 6 }) {Text(this.item.name).fontSize(16).fontWeight(FontWeight.Bold)Text(`${this.item.weight} 克`).fontSize(14).fontColor('#777')}.width('40%').alignItems(HorizontalAlign.Start)Column({ space: 6 }) {Text(`${this.item.calorie * this.item.count} 卡`).fontSize(16).fontColor('#555')Row() {Text('-').fontSize(20).width(25).height(25).textAlign(TextAlign.Center).borderRadius(4).border({ width: 1, color: '#ccc' }).onClick(() => {if (this.item.count > 0) {this.item.count--;this.ifjiajian = false;this.calorieNUm();this.weightNUm();}})Text(`${this.item.count}`).fontSize(16).width(30).textAlign(TextAlign.Center)Text('+').fontSize(20).width(25).height(25).textAlign(TextAlign.Center).borderRadius(4).border({ width: 1, color: '#ccc' }).onClick(() => {this.item.count++;this.ifjiajian = true;this.calorieNUm();this.weightNUm();})}.justifyContent(FlexAlign.SpaceAround).width(90)}.width('40%').alignItems(HorizontalAlign.Center)}.width('100%').padding({ left: 10, right: 10 }).justifyContent(FlexAlign.SpaceBetween)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MQ选型及RocketMQ架构总览

一、什么是MQ MQ(MessageQueue) Message(消息):消息是在不同进程之间传递的数据,这些进程可以在同一台机器上,也可以在不同的机器上。 Queue(队列):队列原意是指一种具有FIFO&#…

python与java的区别

java老程序员来学习python了,记录一下两种类型语言的区别: Python与Java变量类型对比 python里面定义变量不需要指定变量的数据类型,并且是可以修改成其他类型java里面定义变量要指定变量的数据类型,指定以后不可以修改成其他数据…

固件签名技术深度解析:HSM模块如何守护设备安全,CAS系统如何赋能产业升级

引言:数字时代的固件安全危机 在万物互联的今天,全球设备固件安全事件频发:某汽车品牌因固件漏洞导致百万车辆被远程控制,某医疗设备厂商因固件篡改引发数据泄露,某工业控制系统因非法固件升级造成生产线瘫痪……这些…

修改Typora快捷键

代码 的默认快捷键为: 这对我来说不太友好,太难按了,而且我电脑右边的Ctrl键坏了,这意味着我只能一个左手去按这3个键的组合,这更是难上加难了,于是想到改一下快捷键,代码块 是Ctrl Shift K&…

Bellman-Ford算法(详解版)

Bellman-Ford算法 Bellman-Ford算法是用来解决,对于有负权的图的**单源最短路径**.因为DJ算法不可以解决对于负权的图,所以使用这个算法来求解.但是依旧不可以有负回路.因为负回路就没有存在单源最短路径这一说. BF的另一个重要的用途就是用来检测**是不是存在负回路** 思路…

《HarmonyOSNext的ForEach数组渲染の核心玩法与避坑指南》

《HarmonyOSNext教学宝典:ForEach数组渲染全攻略与性能优化》 #HarmonyOS开发 #ArkTS实战 #组件解析 🎯 ForEach组件完全指南:数组循环渲染核心机制 举个栗子🌰: ForEach相当于智能印刷机,将数组元素自动转…

单片机 - STM32F407 ADC 模式详解:单次转换、连续转换、扫描模式、非扫描模式

STM32F407 ADC 模式详解:单次转换、连续转换、扫描模式、非扫描模式 前言 在 STM32F407 中,ADC(模数转换器)模块常用于采集模拟信号,比如读取光敏电阻、电压、电流、温度传感器等。STM32 的 ADC 模式较多&#xff0c…

开源模型应用落地-工具使用篇-从零开始搭建Qdrant Web UI-可视化管理工具-Windows(十)

一、前言 Qdrant 是一个高性能的向量搜索引擎,广泛应用于相似性搜索、推荐系统和大规模数据检索等场景。虽然其原生 API 提供了强大的功能,但对于开发者和运维人员来说,缺乏直观的可视化界面常常增加了使用门槛。为了解决这一问题&#xff0c…

高频交易技术:订单簿分析与低延迟架构——从Level 2数据挖掘到FPGA硬件加速的全链路解决方案

高频交易技术:订单簿分析与低延迟架构——从Level 2数据挖掘到FPGA硬件加速的全链路解决方案 一、引言:高频交易的技术本质 1.1 速度即利润的微观战场 数据揭示:据NYSE实测,每降低1微秒延迟可获得年化$700-1500万套利窗口&#…

基于生成对抗网络(GAN)的图像生成与编辑:原理、应用与实践

前言 生成对抗网络(GAN)是近年来深度学习领域中最具影响力的技术之一。自2014年由Ian Goodfellow等人首次提出以来,GAN已经在图像生成、图像编辑、风格转换等多个领域取得了令人瞩目的成果。GAN的核心思想是通过生成器(Generator&…

pytorch基本运算-梯度运算:requires_grad_(True)和backward()

引言 前序学习进程中,已经对pytorch基本运算中的求导进行了基础讨论,相关文章链接为: 导数运算pytorch基本运算-导数和f-string-CSDN博客 实际上,求导是微分的进一步计算,要想求导的前一步其实是计算微分&#xff1…

idea64.exe.vmoptions配置

这个idea64.exe.vmoptions文件是用于配置 IntelliJ IDEA(64位版本)运行时的 Java 虚拟机(JVM)参数。这些参数直接影响到 IDEA 的性能、内存使用、调试能力和行为。 下面是对文件中每一行配置的详细解读: -Xms2048m 作…

齐次变换矩阵相乘的复合变换:左乘与右乘的深度解析

在三维几何变换中,齐次变换矩阵相乘是实现复杂变换的核心方法。本文将通过一个包含四个变换步骤的完整示例,深入探讨齐次变换矩阵左乘和右乘的区别,并结合 Python sympy 库的代码实现,详细阐述变换过程和结果差异。 二维齐次坐标的旋转变换 在二维齐次坐标系中,一个点可以…

5g LDPC编译码-LDPC编码

目录 1、LDPC编码基础知识 2、5g的LDPC编码 2.1 LDPC分块: 2.2 LDCP编码 2.3 校验位的产生 1、LDPC编码基础知识 LDPC属于线性分组码,线性分组码的基本知识如下: 编码后的码字是由初始二进制序列与生成矩阵在二进制域相乘后得到,生成矩阵与校验矩阵,校验矩阵与编码后…

OpenVINO使用教程--resnet分类模型部署

OpenVINO使用教程--resnet分类模型部署 本节内容模型准备推理测试分析&总结本节内容 OpenVINO 根据AI技术类型将部署任务分成传统模型模型部署和生成式AI模型部署,传统模型指的是各种CNN小模型,这部分部署只需要OpenVINO包,具体安装教程可以参考之前的章节:OpenVINO环境…

无字母数字webshell的命令执行

在Web安全领域,WebShell是一种常见的攻击手段,通过它攻击者可以远程执行服务器上的命令,获取敏感信息或控制系统。而无字母数字WebShell则是其中一种特殊形式,通过避免使用字母和数字字符,来绕过某些安全机制的检测。 …

C++斯特林数在C++中的数学理论与计算实现1

一、 斯特林数概述 1.1 组合数学中的核心地位 斯特林数(Stirling Numbers)是组合数学中连接排列、组合与分划问题的核心工具,分为两类: 第一类斯特林数(Stirling Numbers of the First Kind)&#xff1a…

[C++] STL大家族之<map>(字典)容器(附洛谷)

map-目录 使用方法头文件与声明定义基本操作 使用方法 头文件与声明定义 头文件是: #include <map>我们这样声明一个字典: map</*key_type*/, /*value_type*/> /*map_name*/; // 例子: map<int, char> mp;这里稍作解释: key_type是你每个键值对中的键的…

使用 Flutter 在 Windows 平台开发 Android 应用

以下是完整的开发流程&#xff0c;包括环境搭建、代码实现和应用发布&#xff0c;帮助你开发一个具有地图显示、TCP 通信功能的 Android 应用。 一、环境搭建 1. 安装 Flutter SDK 从 Flutter 官网 下载最新稳定版 SDK解压到本地目录&#xff08;如 D:\flutter&#xff09;添…

【模板】埃拉托色尼筛法(埃氏筛)

一、算法简介 在数论与编程竞赛中&#xff0c;求解 [ 1 , n ] [1,n] [1,n] 范围内的所有质数是常见的基础问题。埃拉托色尼筛法&#xff08;Sieve of Eratosthenes&#xff09; 是一种古老而高效的算法&#xff0c;可以在 O ( n log ⁡ log ⁡ n ) O(n \log \log n) O(nlogl…