鸿蒙开发实践项目--饮食记录卡片(4)

食物列表页

食物列表页,当用户添加食物时,显示一个食物列表页,该页面上方有若干栏目,点击对应栏目,会显示该分类的食物列表,便于用户快速查询并添加食物,点击添加食物时,会弹出面板,输入数字,决定要添加的食物数量是多少,并根据一系列计算,得到卡路里值。

要实现的组件如下:

食物分类栏

食物卡片以及由食物卡组成的列表

便于控制添加食物数量所用到的数字面板

这里先优先创建食物卡片类,ps:后续会添加运动栏,但本质所需用到的属性与食物一致,只不过在最后计算卡路里的时候有所区别,所以也可以使用该类表示运动,不过要添加一个分类属性加以区别

食物分类栏

食物分类栏与制作首页下方导航栏一致,不过没有用到自定义tabs组件,仅使用了文字进行描述,数据模型部分稍后再做解释

Tabs() {
  TabContent() {
    this.TabContentBuilder(ItemModel.list(this.isFood))
  }
  .tabBar('全部')

食物卡片以及组成列表

1.食物卡片

食物卡片布局简单,但是涉及到较多不同单位的食物,单位无法统一,这提醒我们,在建立数据模型的时候,添加一个食物单位的属性

@Builder TabContentBuilder(items: RecordItem[]) {
  List({ space: CommonConstants.SPACE_10 }) {
    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'))
          }.alignItems(HorizontalAlign.Start)

          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%')
}

2.食物列表

ForEach(
  ItemModel.listItemGroupByCategory(this.isFood),
  (group: GroupInfo<ItemCategory, RecordItem>) => {
    TabContent() {
      this.TabContentBuilder(group.items)//食物卡片
    }
    .tabBar(group.type.name)
  })

数字面板

数字面板上半部分为日期以及食物内容, 下半部分是数字键盘以及取消和提交按钮

首先编写上半部分ui

build() {
  Row(){
    Text('2024年1月25日 早餐')
      .fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_600)
    Image($r('app.media.ic_public_spinner'))
      .width(20)
      .fillColor(Color.Black)
  }
}

这里的日期当然也可以使用一些方法调用到系统当前日期

剩下的部分为食物内容

build() {
  Column({space: CommonConstants.SPACE_8}){
    // 1.图片
    Image(this.item.image).width(150)
    // 2.名称
    Row(){
      Text(this.item.name).fontWeight(CommonConstants.FONT_WEIGHT_700)
    }
    .backgroundColor($r('app.color.lightest_primary_color'))
    .padding({top: 5, bottom: 5, left: 12, right: 12})
    Divider().width(CommonConstants.THOUSANDTH_940).opacity(0.6)
    // 3.营养素
    Row({space: CommonConstants.SPACE_8}){
      this.NutrientInfo({label: '热量(千卡)', value: this.item.calorie})
      if(this.item.id < 10000){
        this.NutrientInfo({label: '碳水(千卡)', value: this.item.carbon})
        this.NutrientInfo({label: '蛋白质(千卡)', value: this.item.protein})
        this.NutrientInfo({label: '脂肪(千卡)', value: this.item.fat})//这里是自定义组件简化一下代码,与之前操作类似,就不做过多展示了
      }
    }
    Divider().width(CommonConstants.THOUSANDTH_940).opacity(0.6)
    // 4.数量
    Row(){
      Column({space: CommonConstants.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)
    }
  }
}

然后是键盘部分

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})
}

至此面板所需要的组件已全部完成,开始组装!

build() {
  Column() {
    // 1.头部导航
    this.Header()
    // 2.列表
    ItemList({ showPanel: this.onPanelShow.bind(this), isFood: this.isFood })
      .layoutWeight(1)
    // 3.底部面板
    Panel(this.showPanel) {
      // 3.1.顶部日期
      ItemPanelHeader()
      // 3.2.记录项卡片
      if(this.item){
        ItemCard({amount: this.amount, item: $item})
      }
      // 3.3.数字键盘
      NumberKeyboard({amount: $amount, value: $value})
      // 3.4.按钮
      this.PanelButton()
    }
    .mode(PanelMode.Full)
    .dragBar(false)
    .backgroundMask($r('app.color.light_gray'))
    .backgroundColor(Color.White)
  }
  .width('100%')
  .height('100%')
}

下面解说一下数字面板所要实现的一些业务逻辑,第一部分为通过提交数据实现数据持久化保存

  Row({space: CommonConstants.SPACE_6}){
    Button('取消')
      .panelButtonStyle()
      .backgroundColor($r('app.color.light_gray'))
      .onClick(() => this.showPanel = false)//showPanel为一个布尔类型值,用于控制页面是否出现
    Button('提交')
      .panelButtonStyle()
      .backgroundColor($r('app.color.primary_color'))
      .onClick(() => {
        // 1.持久化保存
        RecordService.insert(this.type.id, this.item.id, this.amount)//将数据保存到关系型数据库
          .then(() => {
            // 2.关闭弹窗
            this.showPanel = false
          })
      })
  }
  .margin({top: 10})
}

 第二,与页面路由有关,点击返回图片,使用router.back的方法,跳转回先前的页面

@Builder Header() {
  Row() {
    Image($r('app.media.ic_public_back'))
      .width(24)
      .onClick(() => router.back())
    Blank()
    Text(this.type.name).fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_600)
  }
  .width(CommonConstants.THOUSANDTH_940)
  .height(32)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值