一 项目目标
综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0及以上版本开发一款具有实用性和创新
性的移动应用软件
二 项目介绍
黑马健康app是一款通过记录饮食,食物营养成分,以及提供运动可消耗热量的app,用户可以查看饮食记录帮助用户清晰的得知自己的个人身体热量与营养的摄入与消耗以及还可摄入多少热量
1,统计卡片
import BreakpointType from '../../common/bean/BreanpointType'
import BreakpointConstants from '../../common/constants/BreakpointConstants'
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import RecordService from '../../service/RecordService'
import RecordVO from '../../viewmodel/RecordVO'
import StatsInfo from '../../viewmodel/StatsInfo'
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())
//prop单向绑定读取appstorage中的日期信息
@StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM
@Consume @Watch('handleRecordsChange') records: RecordVO[]
@State info: StatsInfo = new StatsInfo()
handleRecordsChange(){
this.info = RecordService.calculateStatsInfo(this.records)
}
controller: CustomDialogController = new CustomDialogController({
builder: DatePickDialog({selectedDate: new Date(this.selectedDate)})
//读取15行中的日期
})
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(){ //滑动组件
CalorieStats({intake: this.info.intake, expend: this.info.expend})
// 2.1.热量统计
NutrientStats({carbon: this.info.carbon, protein: this.info.protein, fat: this.info.fat}) // 2.2.营养素统计
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(CommonConstants.DEFAULT_18)
.indicatorStyle({selectedColor: $r('app.color.primary_color')})
.displayCount(new BreakpointType({
sm: 1,
md: 1,
lg: 2
}).getValue(this.currentBreakpoint))
}
.width(CommonConstants.THOUSANDTH_940)
.backgroundColor($r('app.color.stats_title_bgc'))
.borderRadius(CommonConstants.DEFAULT_18)
}
}
卡路里显示组件
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct CalorieStats {
@Prop intake: number
@Prop expend: number
recommend: number = CommonConstants.RECOMMEND_CALORIE
remainCalorie(){
return this.recommend - this.intake + this.expend
}
build() {
Row({space: CommonConstants.SPACE_6}){
// 1.饮食摄入
this.StatsBuilder({label: '饮食摄入', value: this.intake})
// 2.还可以吃
Stack(){
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'))
}
}
}
}
营养显示组件
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NutrientStats {
@Prop carbon: number
@Prop protein: number
@Prop fat: number
recommendCarbon: number = CommonConstants.RECOMMEND_CARBON
recommendProtein: number = CommonConstants.RECOMMEND_PROTEIN
recommendFat: number = CommonConstants.RECOMMEND_FAT
build() {
Row({space: CommonConstants.SPACE_6}){
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%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({top: 30, bottom: 35})
}
@Builder StatsBuilder($$:{label: string, value: number, recommend: number, color: ResourceStr}){
Column({space: CommonConstants.SPACE_6}){
Stack(){
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'))
}
}
}
总结
1,统计卡片:
引入了多个模块和工具类,包括断点类型、常量、日期工具、记录服务、记录视图模型、统计信息视图模型以及对话框和统计子组件。
selectedDate: 当前选中的统计日期,使用@StorageProp装饰器与本地存储同步,初始化为当天的开始时间。
currentBreakpoint: 当前设备屏幕断点类型,用于响应式布局。
records: 通过@Consume装饰器消费的记录列表,用于计算统计信息。
info: 存储统计信息的对象,初始化为空的StatsInfo实例。
controller: 控制日期选择对话框的控制器,初始时使用当前选中日期创建DatePickDialog。
方法定义:
handleRecordsChange: 监听records变化,当记录列表更新时重新计算统计信息并更新info。
构建UI界面:
使用Column构建主布局,包含日期行:
显示所选日期的文本和一个可点击的图标,点击后打开日期选择对话框。
滑动组件(Swiper):
根据屏幕断点展示不同的统计子组件:
CalorieStats: 热量摄入与消耗的统计。
NutrientStats: 营养素(碳水、蛋白质、脂肪)的统计。
滑动组件的样式可根据屏幕断点动态调整显示数量。
应用了内外边距、宽度、背景色、边框半径等样式,以美化组件外观并实现响应式布局。
滑动指示器的颜色根据应用主题调整。
2,卡路里显示组件
属性定义:
intake: 通过@Prop装饰器接收的热量摄入数值。
expend: 同样通过@Prop装饰器接收的热量消耗数值。
recommend: 基于CommonConstants.RECOMMEND_CALORIE的推荐热量摄入值。
计算方法:
remainCalorie: 计算剩余可摄入热量,即推荐摄入值减去实际摄入值加上消耗值。
UI构建方法 (build):
使用Row布局展示热量摄入、可摄入剩余量(结合进度条)和热量消耗三个部分。
热量摄入与消耗: 分别调用StatsBuilder构建标签和数值展示。
“还可以吃”部分: 结合Stack布局嵌套Progress(环形进度条)和通过StatsBuilder构建的剩余热量提示。进度条展示摄入热量占推荐摄入值的比例。
内部构建器方法 (StatsBuilder):
接收label(标签名)、value(数值)和可选的tips(提示信息)参数,构建一个列布局展示统计数据。
包含标签文本、数值文本,以及如果提供的额外提示信息。
文本样式根据用途(标签、数值、提示)进行了差异化设置,例如字体大小、颜色、加粗等。
样式与布局:
使用了外部传入的CommonConstants来统一控制间距、颜色、字体等样式,确保组件风格与应用保持一致。
利用Flex布局(justifyContent: FlexAlign.SpaceEvenly)使各部分在行内均匀分布,以及内外边距调整组件对齐和间距。
3,营养显示组件
属性定义:
carbon, protein, fat: 分别代表碳水化合物、蛋白质、脂肪的摄入量,通过@Prop装饰器接收。
recommendCarbon, recommendProtein, recommendFat: 分别代表这三种营养素的推荐摄入量,使用常量定义。
构建方法 (build):
使用Row布局横向排列三个营养素的统计展示。
对每种营养素调用内部构建器方法StatsBuilder,传递相应的营养素标签、摄入值、推荐摄入值以及代表该营养素的颜色资源。
内部构建器方法 (StatsBuilder):
接收营养素的详细信息(标签、值、推荐值、颜色)。
构建包含环形进度条的布局,进度条表示实际摄入量与推荐摄入量的比例,颜色根据营养素类型变化。
显示“摄入推荐”的文本和具体的摄入/推荐数值比。
最后显示营养素名称和单位(克)。
样式与布局:
利用Row布局确保营养素展示在一行内,并通过justifyContent: FlexAlign.SpaceEvenly使它们之间保持均匀的间隔。
进度条宽度、边框宽度等样式通过常量统一控制,保持视觉一致性。
文本样式如字体大小、颜色、加粗等也进行了细致设定,以增强信息的可读性和美观性