鸿蒙实战项目-黑马健康应用开发日志三

目录

前言

一、项目目标

二、项目介绍

三、具体页面实现

1.饮食记录页面--记录列表

(1)页面分析:

(2)页面实现代码:

(3)运行截图:

2.食物列表页

(1)页面分析:

(2)页面实现代码:

(3)运行截图:

3.食物列表-底部Panel

(1)页面分析:

(2)页面实现代码:

(3)运行截图:

总结


前言


综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0及以上版本开发一款具有实用性和创新 性的移动应用软件。

一、项目目标


综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0及以上版本开发一款具有实用性和创新  性的移动应用软件。

二、项目介绍


黑马健康是一款功能全面的健康管理应用,它通过提供个性化的饮食记录、健康评估等功能,帮助用户轻松管理健康,改善饮食和生活习惯。无论是需要减肥塑形,还是关注日常营养摄入,黑马健康都能为用户提供定制化的服务,让健康管理变得简单而有效。

        

三、具体页面实现

1.饮食记录页面--记录列表

(1)页面分析:

本页面每个卡片都相似,可以编写一个卡片使用ForEach和List列表循环遍历渲染

(2)页面实现代码:
import { CommonConstants } from '../../common/constants/CommonConstants'
@Extend(Text) function grayText(){
  .fontSize(14)
  .fontColor($r('app.color.light_gray'))
}

@Component
export default struct RecordList {
  build() {
    List({space:CommonConstants.SPACE_10}){
      ForEach([1,2,3,4],(item)=>{
        ListItem(){
          Column(){
            //标题
            Row({space:CommonConstants.SPACE_4}){
              Image($r('app.media.ic_breakfast'))
                .width(24)
              Text('早餐')
                .fontSize(18)
                .fontWeight(CommonConstants.FONT_WEIGHT_700)
              Text('建议....')
                .grayText()
              Blank()
              Text('212')
                .fontSize(14)
                .fontColor($r('app.color.primary_color'))
              Text('千卡')
                .grayText()
              Image($r('app.media.ic_public_add_norm_filled'))
                .width(20)
                .fillColor($r('app.color.primary_color'))

            }
            .width('100%')
            //记录列表
            List(){
              ForEach([1,2],(item)=>{
                ListItem(){
                  Row({space:CommonConstants.SPACE_6}){
                    Image($r('app.media.toast'))
                      .width(50)
                    Column({space:CommonConstants.SPACE_4}){
                      Text('全麦吐司')
                        .fontWeight(CommonConstants.FONT_WEIGHT_500)
                      Text('一片')
                        .grayText()
                    }
                    Blank()
                    Text('91千卡')
                      .grayText()
                  }
                  .width('100%')
                  .padding(CommonConstants.SPACE_6)
                }
                .swipeAction({end:this.deleteButton.bind(this)})
              })
            }
            .width('100%')
          }
          .width('100%')
          .backgroundColor(Color.White)
          .borderRadius(CommonConstants.DEFAULT_18)
          .padding(CommonConstants.SPACE_12)
        }
      })
    }
    .width(CommonConstants.THOUSANDTH_940)
    .height('100%')
    .margin({top:10})
  }
  @Builder deleteButton(){
    Image($r('app.media.ic_public_delete_filled'))
      .width(20)
      .height(20)
      .fillColor(Color.Red)
      .margin(5)
  }
}
(3)运行截图:

2.食物列表页

(1)页面分析:

食物列表页面实现记录列表页面,用于提供各种食物,并且点击右方的绿色加号按钮就会跳转到食物列表页,显示出要添加食物的具体信息。

(2)页面实现代码:

ItemList.ets

import { CommonConstants } from '../../common/constants/CommonConstants'
import ItemModel from '../../model/ItemModel'
import GroupInfo from '../../viewmodel/GroupInfo'
import RecordItem from '../../viewmodel/RecordItem'
@Component
export default struct ItemList {

  showPanel:(item:RecordItem) => void
  @State isFood:boolean =true

  build() {
    Tabs(){
      TabContent(){
        this.TabContentBuilder(ItemModel.list(this.isFood))
      }
      .tabBar('全部')
      
      ForEach(ItemModel.listItemGroupByCategory(this.isFood),(group:GroupInfo)=>{
        TabContent(){
          this.TabContentBuilder(group.items)
        }
        .tabBar(group.type.name)
      })
    }
    .width('94%')
    .height('100%')
    .barMode(BarMode.Scrollable)
  }
  @Builder TabContentBuilder(items:RecordItem[]){
    List({space:CommonConstants.SPACE_8}){
      ForEach(items,(item:RecordItem)=>{
        ListItem(){
          Row({space:CommonConstants.SPACE_6}){
            Image(item.image)
              .width(50)
            Column({space:CommonConstants.SPACE_4}){
              Text(item.name)
                .fontWeight(CommonConstants.FONT_WEIGHT_500)
              Text(`${item.calorie}千卡/${item.unit}`)
                .fontSize(14)
                .fontColor($r('app.color.light_gray'))
            }
            Blank()
            Image($r('app.media.ic_public_add_norm_filled'))
              .width(18)
              .fillColor($r('app.color.primary_color'))
          }
          .width('100%')
          .padding(CommonConstants.SPACE_6)
        }
        .onClick(() => this.showPanel(item))
      })
    }
    .width('100%')
    .height('100%')
  }
}
(3)运行截图:

3.食物列表-底部Panel

(1)页面分析:

当你点击你想了解的具体食物信息以及添加食物数量时,会从页面底部弹出一个面板,该面板是由Panel组件实现的,该面板页面主要分为四大块,顶部日期,记录项卡片,数字键盘,以及按钮。

食物列表页面实现记录列表页面点击右方的绿色加号按钮就会会从页面底部弹出一个面板,显示出要添加食物的具体信息。面板使用Panel组件实现。面板包括日期,食物详细信息,数字键盘,按钮。

(2)页面实现代码:

ItemCard.ets

import { CommonConstants } from '../../common/constants/CommonConstants'
import RecordItem from '../../viewmodel/RecordItem'
@Component
export default struct ItemCard {
  @Prop amount:number
  @Link item:RecordItem
  build() {
    Column({space:8}){
      //图片
      Image(this.item.image)
        .width(150)
      //名称
      Row() {
        Text(this.item.name)
          .fontWeight(CommonConstants.FONT_WEIGHT_700)
      }
      .backgroundColor($r('app.color.lightest_primary_color'))
      .padding({top:5,bottom:5,left:10,right:10})
      Divider()
        .width(CommonConstants.THOUSANDTH_940)
        .opacity(0.6)
      //营养素
      Row({space:8}){
        this.NutrientInfo('热量(千卡)',this.item.calorie)
        if (this.item.id<10000){
          this.NutrientInfo('碳水(克)',this.item.carbon)
          this.NutrientInfo('蛋白质(克)',this.item.protein)
          this.NutrientInfo('脂肪(克)',this.item.fat)
        }
      }
      Divider()
        .width(CommonConstants.THOUSANDTH_940)
        .opacity(0.6)
      //数量
      Row(){
        Column({space:4}){
          Text(this.amount.toFixed(1))
            .fontSize(50)
            .fontColor($r('app.color.primary_color'))
            .fontWeight(CommonConstants.FONT_WEIGHT_600)
          Divider()
            .color($r('app.color.primary_color'))
            .width(150)
        }
        Text(this.item.unit)
          .fontColor($r('app.color.light_gray'))
          .fontWeight(CommonConstants.FONT_WEIGHT_600)
      }
    }
  }
  @Builder NutrientInfo(lable:string,value:number){
    Column({space:8}){
      Text(lable)
        .fontSize(14)
        .fontColor($r('app.color.light_gray'))
      Text((value*this.amount).toFixed(1))
        .fontWeight(CommonConstants.FONT_WEIGHT_700)
    }
  }
}

ItemList.ets

import { CommonConstants } from '../../common/constants/CommonConstants'
import ItemModel from '../../model/ItemModel'
import GroupInfo from '../../viewmodel/GroupInfo'
import RecordItem from '../../viewmodel/RecordItem'
@Component
export default struct ItemList {

  showPanel:(item:RecordItem) => void
  @State isFood:boolean =true

  build() {
    Tabs(){
      TabContent(){
        this.TabContentBuilder(ItemModel.list(this.isFood))
      }
      .tabBar('全部')
      
      ForEach(ItemModel.listItemGroupByCategory(this.isFood),(group:GroupInfo)=>{
        TabContent(){
          this.TabContentBuilder(group.items)
        }
        .tabBar(group.type.name)
      })
    }
    .width('94%')
    .height('100%')
    .barMode(BarMode.Scrollable)
  }
  @Builder TabContentBuilder(items:RecordItem[]){
    List({space:CommonConstants.SPACE_8}){
      ForEach(items,(item:RecordItem)=>{
        ListItem(){
          Row({space:CommonConstants.SPACE_6}){
            Image(item.image)
              .width(50)
            Column({space:CommonConstants.SPACE_4}){
              Text(item.name)
                .fontWeight(CommonConstants.FONT_WEIGHT_500)
              Text(`${item.calorie}千卡/${item.unit}`)
                .fontSize(14)
                .fontColor($r('app.color.light_gray'))
            }
            Blank()
            Image($r('app.media.ic_public_add_norm_filled'))
              .width(18)
              .fillColor($r('app.color.primary_color'))
          }
          .width('100%')
          .padding(CommonConstants.SPACE_6)
        }
        .onClick(() => this.showPanel(item))
      })
    }
    .width('100%')
    .height('100%')
  }
}

ItemPanelHeader.ets

import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import DatePickDialog from '../record/DatePickDialog'
@Component
export default struct ItemPanelHeader {
  @StorageProp ('selectedDate') selectedDate : number = DateUtil.beginTimeOfDay(new Date())

  controller:CustomDialogController = new CustomDialogController({
    builder:DatePickDialog({selectedDate:new Date(this.selectedDate)})
  })

  build() {
    Row({space:10}){
      Text(DateUtil.formatDate(this.selectedDate))
        .onClick(()=>this.controller.open())
        .fontSize(18)
        .fontWeight(CommonConstants.FONT_WEIGHT_600)
      Text('早餐')
        .fontSize(18)
        .fontWeight(CommonConstants.FONT_WEIGHT_600)

      Image($r('app.media.ic_public_spinner'))
        .width(20)
        .fillColor(Color.Black)
    }
  }
}

NumberKeyboard.ets

import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NumberKeyboard {
  @Link amount:number
  @Link value:string

  numbers:string[] = ['1','2','3','4','5','6','7','8','9','0','.']
  @Styles keyboxStyle(){
    .backgroundColor(Color.White)
    .height(60)
    .borderRadius(8)
  }
  build() {
    Grid(){
      ForEach(this.numbers,num =>{
        GridItem(){
          Text(num)
            .fontSize(20)
            .fontWeight(CommonConstants.FONT_WEIGHT_900)
        }
        .keyboxStyle()
        .onClick(()=>{
          this.clickNumber(num)
        })
      })
      GridItem(){
        Text('删除')
          .fontSize(20)
          .fontWeight(CommonConstants.FONT_WEIGHT_900)
      }
      .keyboxStyle()
      .onClick(()=>{
        this.clickDelete()
      })
    }
    .width('100%')
    .height(280)
    .backgroundColor($r('app.color.index_page_background'))
    .columnsTemplate('1fr 1fr 1fr')
    .columnsGap(8)
    .rowsGap(8)
    .padding(8)
    .margin({top:10})
  }
  clickNumber(num:string){
    //拼接
    let val = this.value+num
    //校验
    let firstIndex = val.indexOf('.')
    let lastIndex = val.lastIndexOf('.')
    if (firstIndex!==lastIndex||(lastIndex!=-1&&lastIndex<val.length-2)) {
      return
    }
    //转为数值
    let amount = this.parseFloat(val)
    //保存
    if (amount>=999.9) {
      this.amount=999.0
      this.value='999'
    }else {
      this.amount = amount
      this.value = val
    }
  }
  clickDelete(){
    if (this.value.length<=0) {
      this.value=''
      this.amount=0
      return
    }
    this.value=this.value.substring(0,this.value.length-1)
    this.amount=this.parseFloat(this.value)
  }
  parseFloat(str:string){
    if (!str){
      return 0
    }
    if (str.endsWith('.')) {
      str = str.substring(0,str.length-1)
    }
    return parseFloat(str)
  }
}

(3)运行截图:


总结

通过对黑马程序员鸿蒙实战项目相关课程的学习,加深了对List列表,Tabs组件的认识;更好的学会了Panel组件与Grid组件的使用方法,成功使用相关组件解决了底部弹窗的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值