一、项目目标
综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0及以上版本开发一款具有实用性和创新 性的移动应用软件。
二、项目介绍
黑马健康是一款功能全面的健康管理应用,它通过提供个性化的饮食记录、健康评估等功能,帮助用户轻松管理健康,改善饮食和生活习惯。无论是需要减肥塑形,还是关注日常营养摄入,黑马健康都能为用户提供定制化的服务,让健康管理变得简单而有效。
饮食部分UI设计
顶部导航栏设计
1.
2.
3.
4.第一部分搜索栏以及邮箱
5.
统计卡片
1.
2.
3.
4.
5.
6.CalorieStats组件
遇到的问题及解决方案
1.搜索栏及邮箱提示API方法的设置
Search({placeholder: '搜索饮食或运动信息'})
.textFont({size: 18})
.layoutWeight(1)
Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){ Image($r('app.media.ic_public_email'))
.width(24)
search方法提供搜索栏,Badge提供角标
2.开发饮食记录UI界面时,实现顶部搜索栏使用日期滑动选择器
3.如何把日期选择器的日期保存到全局
// 1.保存日期到全局存储 AppStorage.SetOrCreate('selectedDate', this.selectedDate.getTime())
getTime()转成ms值去计算,selectedDate存到AppStorage
4.日期不一致
@StorageProp('selectedDate') selectedDate: number = DateUtil.beginTimeOfDay(new Date())
DateUtil.beginTimeOfDay(new Date()获取开始日期
把日期回显回去,在对话框补全,读取对话款的选中日期
builder: DatePickDialog({selectedDate: new Date(this.selectedDate)})
日期格式化展示时,使用selectedDate
Text(DateUtil.formatDate(this.selectedDate)) 把ms格式化成日期
5.如何让设置进度条
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}`}) }
使用Stack,为环绕关系
你最想分享的亮点
1.首页UI设计
Tabs组件可以实现页面内视图内容快速切换,包含TabBar和TabContent两个部分,
barPosition控制位置,.vertical控制水平还是竖直
Builder用来调整样式,可以改成文字加图片的样式,不使用自定义模板
代码
Index
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(){
// 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'))
}
}
}
}
WelcomePage
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(){
// 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'))
}
}
}
}
RecordIndex
import SearchHeader from './SearchHeader'
import StatsCard from './StatsCard'
@Component
export default struct RecordIndex{
build() {
Column(){
// 1.头部搜索栏
SearchHeader()
// 2.统计卡片
StatsCard()
// 3.记录列表
Text('记录列表')
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.index_page_background'))
}
}
SearchHeader
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct SearchHeader {
build() {
Row({space: CommonConstants.SPACE_6}){
Search({placeholder: '搜索饮食或运动信息'})
.textFont({size: 18})
.layoutWeight(1)
Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){
Image($r('app.media.ic_public_email'))
.width(24)
}
}
.width(CommonConstants.THOUSANDTH_940)
}
}
StatsCard
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import DatePickDialog from './DatePickDialog'
@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(){
}
}
.width(CommonConstants.THOUSANDTH_940)
.backgroundColor($r('app.color.stats_title_bgc'))
.borderRadius(CommonConstants.DEFAULT_18)
}
}
DatePickDialog
import { CommonConstants } from '../../common/constants/CommonConstants'
@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)
}
}
CalorieStats
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(){
// 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'))
}
}
}
}
NutrientStats
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(){
// 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'))
}
}
}
}