鸿蒙实战案例(三)

一、食物列表页

点击食物后面的加号时,会跳出一个食物面板,这个面板上会跳出具体的食物信息。

例如,现在我们就要完成这个界面。整个列表页是一个列式布局

代码为:

大纲:


import router from '@ohos.router'
import { CommonConstants } from '../common/constants/CommonConstants'
import { RecordTypeEnum } from '../model/RecordTypeModel'
import RecordService from '../service/RecordService'
import ItemCard from '../view/item/ItemCard'
import itemList from '../view/item/itemList'
import ItemPanelHeader from '../view/item/ItemPanelHeader'
import NumberKeyboard from '../view/item/NumberKeyboard'
import RecordItem from '../viewmodel/RecordItem'
import RecordType from '../viewmodel/RecordType'
@Entry
@Component
struct ItemIndex {
  @State amount:number = 1
  @State value:string=''
  @State showPanel:boolean = false
  @State item:RecordItem = null
  @State type:RecordType = RecordType[0]
  @State isFood:boolean = true


//控制弹窗展示
  onPanelShow(item:RecordItem){
    this.amount = 1
    this.value = ''
    this.showPanel = true
    this.item = item
  }

  //生命周期钩子
  onPageShow(){
    //1.获取跳转时参数
    let params:any = router.getParams()
    //2.获取点击饮食记录类型
    this.type = params.type
    this.isFood = this.type.id !== RecordTypeEnum.WORKOUT
  }


  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按钮
        Row({space:CommonConstants.SPACE_6}){
          Button('取消')
            .width(120)
            .backgroundColor($r('app.color.light_gray'))
            .type(ButtonType.Normal)
            .borderRadius(6)
            .onClick(()=>this.showPanel = false)
          Button('提交')
            .width(120)
            .backgroundColor($r('app.color.light_gray'))
            .type(ButtonType.Normal)
            .borderRadius(6)
            .onClick(()=>{
              //1.持久化保存
              RecordService.insert(this.type.id,this.item.id,this.amount)
                .then(()=>{
                  //2.关闭弹窗
                  this.showPanel = false
                })
            })
        }
        .margin({top:10})
      }
      .mode(PanelMode.Full)
      .dragBar(false)
      .backgroundMask($r('app.color.light_gray'))
      .backgroundColor(Color.White)
    }
    .width('100%')
    .height('100%')
  }

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

列表信息代码部分:

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

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

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

      ForEach(ItemModel.listItemGroupByCategory(this.isFood), (group:GroupInfo<ItemCategory,RecordItem>)=>{
            TabContent(){
            this.TabContentBuilder(group.items)
          }
        .tabBar(group.type.name)
      })
    }
    .width(CommonConstants.THOUSANDTH_940)
    .height('100%')
    .barMode(BarMode.Scrollable)
  }

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

运行结果

 

二、 弹窗设计

分析得知,弹窗中为一行一行的组件。

代码为

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按钮
        Row({space:CommonConstants.SPACE_6}){
          Button('取消')
            .width(120)
            .backgroundColor($r('app.color.light_gray'))
            .type(ButtonType.Normal)
            .borderRadius(6)
            .onClick(()=>this.showPanel = false)
          Button('提交')
            .width(120)
            .backgroundColor($r('app.color.light_gray'))
            .type(ButtonType.Normal)
            .borderRadius(6)
            .onClick(()=>{
              //1.持久化保存
              RecordService.insert(this.type.id,this.item.id,this.amount)
                .then(()=>{
                  //2.关闭弹窗
                  this.showPanel = false
                })
            })
        }
        .margin({top:10})
      }
      .mode(PanelMode.Full)
      .dragBar(false)
      .backgroundMask($r('app.color.light_gray'))
      .backgroundColor(Color.White)

运行结果

 

三、数字键盘设计

数字键盘用到了一个新的组件Grid组件,是一个网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。

代码

//数字键盘
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NumberKeyboard {

  numbers:string[] = ['1','2','3','4','5','6','7','8','9','0','.']
  @Link amount:number
  @Link value:string

  @Styles keyBoxStyle(){
    .backgroundColor(Color.White)
    .borderRadius(8)
    .height(60)
  }
  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){
    //1.拼接用户输入的内容
    let val = this.value + num
    //2.校验输入格式是否正确
    let firstIndex = val.indexOf('.')
    let lastIndex = val.lastIndexOf('.')
    if (firstIndex !== lastIndex || (lastIndex !=-1 && lastIndex < val.length - 2)) {
    //非法输入
    return
    }
    //3.将字符串转为数值
    let amount = this.parseFloat(val)
    //4.保存
    if (amount>=999.9) {
      this.amount = 999
      this.value = '999'
    }else {
      this.amount = amount
      this.value = val
    }
  }
  clickDelete(){
    if (this.value.length <= 0) {
      return
    }
    this.value = this.value.substring(0,this.value.length - 1)
    this.amount = this.parseFloat(this.value)
  }
//检验是否出现2.这种情况
  parseFloat(str:string) {
    if (!str){
      return 0
    }
    if (str.endsWith('.')) {
      str = str.substring(0, str.length - 1)
    }
    return parseFloat(str)
  }
}

运行结果

 

  • 19
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值