鸿蒙开发———黑马健康app(UI首页开发)

通过 @Link 或 @Prop 装饰器将父组件或外部传入的数据绑定到子组件。底部导航栏,通常使用 Tabs 组件,允许用户在不同的功能页面间切换。 router 模块来处理页面间的导航和跳转,例如 router.pushUrl 或 router.replaceUrl。利用生命周期方法,如 aboutToAppear 和 aboutToDisappear,来处理页面显示和隐藏时的逻辑。:使用 @StorageProp 装饰器或 AppStorage API 来存储和读取用户的应用状态。使用 List 或 ForEach 组件遍历项目列表,为每个项目项创建 ListItem的组件。

这一个页面分为三块小部分
build() {
    Column(){
      // 1.头部搜索栏
      SearchHeader()
      // 2.统计卡片
      StatsCard()
      // 3.记录列表
      RecordList()
        .layoutWeight(1)
    }

  }
}

又分成了三个不同的页面来引用


import BreakpointType from '../common/bean/BreakanpointType'
import BreakpointConstants from '../common/constants/BreakpointConstants'

import { CommonConstants } from '../common/constants/CommonConstants'

import BreakpointSystem from '../common/utils/BreakpointSystem'

import RecordIndex from '../view/record/RecordIndex'

@Entry
@Component
struct Index {
  // 使用@State装饰器定义当前选中的标签页索引,默认为0
  @State currentIndex: number = 0
  // 创建BreakpointSystem实例,用于处理断点逻辑
  private breakpointSystem: BreakpointSystem = new BreakpointSystem()
  // 使用@StorageProp装饰器定义当前断点的存储属性,默认为小型设备断点
  @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM

  // 使用@State装饰器定义页面是否显示的状态
  @State isPageShow: boolean = false

  // 页面显示时调用的方法,设置页面显示状态为true
  onPageShow(){
    this.isPageShow = true
  }
  // 页面隐藏时调用的方法,设置页面显示状态为false
  onPageHide(){
    this.isPageShow = false
  }

  // 定义TabBarBuilder方法,用于构建标签栏的每一项
  @Builder TabBarBuilder(title: ResourceStr, image: ResourceStr, index: number) {
    Column({ space: CommonConstants.SPACE_8 }) {
      // 设置图标,根据当前选中项改变颜色
      Image(image)
        .width(22)
        .fillColor(this.selectColor(index))
      // 设置标题,根据当前选中项改变颜色
      Text(title)
        .fontSize(14)
        .fontColor(this.selectColor(index))
    }
  }

  // 组件即将显示时调用的方法,注册断点监听
  aboutToAppear(){
    this.breakpointSystem.register()
  }

  // 组件即将消失时调用的方法,注销断点监听
  aboutToDisappear(){
    this.breakpointSystem.unregister()
  }

  // 根据当前选中项的索引选择颜色的方法
  selectColor(index: number) {
    // 如果是当前选中项,则返回主题颜色,否则返回灰色
    return this.currentIndex === index ? $r('app.color.primary_color') : $r('app.color.gray')
  }

  // 构建UI界面的方法
  build() {
    // 使用Tabs组件创建标签页,设置标签栏位置
    Tabs({ barPosition: BreakpointConstants.BAR_POSITION.getValue(this.currentBreakpoint) }) {
      // 定义每个标签页的内容和对应的标签栏项
      TabContent() {
        RecordIndex({isPageShow: this.isPageShow})
      }
      // 使用TabBarBuilder构建标签栏的第一项
      .tabBar(this.TabBarBuilder($r('app.string.tab_record'), $r('app.media.ic_calendar'), 0))

      TabContent() {
        // 第二个标签页的内容,这里只是一个示例文本
        Text('发现页面')
      }
      // 使用TabBarBuilder构建标签栏的第二项
      .tabBar(this.TabBarBuilder($r('app.string.tab_discover'), $r('app.media.discover'), 1))

      TabContent() {
        // 第三个标签页的内容,这里只是一个示例文本
        Text('我的主页')
      }
      // 使用TabBarBuilder构建标签栏的第三项
      .tabBar(this.TabBarBuilder($r('app.string.tab_user'), $r('app.media.ic_user_portrait'), 2))
    }
    // 设置Tabs组件的宽度和高度
    .width('100%')
    .height('100%')
    // 设置选项卡变化时的回调,更新当前选中项索引
    .onChange(index => this.currentIndex = index)
    // 设置Tabs组件的布局方向,根据断点类型变化
    .vertical(new BreakpointType({
      sm: false,
      md: true,
      lg: true
    }).getValue(this.currentBreakpoint))
  }
}

头部搜索栏


import { CommonConstants } from '../../common/constants/CommonConstants'


@Component
export default struct SearchHeader {

  build() {
    // 使用Row组件创建水平布局,space属性定义子组件之间的间距
    Row({space: CommonConstants.SPACE_6}){
      // 在Row中添加Search组件用于搜索功能,placeholder属性定义搜索框的占位符文本
      Search({placeholder: '搜索饮食或运动信息'})
        // 使用textFont方法设置搜索框内文字的样式,这里设置了文字大小为18
        .textFont({size: 18})
        // 使用layoutWeight方法设置搜索组件在Row中的权重,值为1表示占据大部分空间
        .layoutWeight(1)
      // 在Row中添加Badge组件,用于显示角标
      Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}})
        // 在Badge内部添加Image组件,用于显示图标
        Image($r('app.media.ic_public_email'))
          // 设置图标的宽度为24
          .width(24)
    }
    // 设置Row组件的宽度为CommonConstants.THOUSANDTH_940定义的值
    .width(CommonConstants.THOUSANDTH_940)
  }
}

统计卡片


import BreakpointType from '../../common/bean/BreakpointType'

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'


@Preview
@Component
export default struct StatsCard {


  @StorageProp('selectedDate') selectedDate: number = DateUtil.beginTimeOfDay(new Date())

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

  // 构建组件UI界面的方法
  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(){
        // 2.1.热量统计组件
        CalorieStats({intake: this.info.intake, expend: this.info.expend})
        // 2.2.营养素统计组件
        NutrientStats({carbon: this.info.carbon, protein: this.info.protein, fat: this.info.fat})
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(CommonConstants.DEFAULT_18)
      // 设置Swiper的指示器样式
      .indicatorStyle({selectedColor: $r('app.color.primary_color')})
      // 根据断点类型设置Swiper的显示数量
      .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 router from '@ohos.router'
import { CommonConstants } from '../../common/constants/CommonConstants'
import RecordService from '../../service/RecordService'
import GroupInfo from '../../viewmodel/GroupInfo'
import RecordType from '../../viewmodel/RecordType'
import RecordVO from '../../viewmodel/RecordVO'

@Extend(Text) function grayText(){
  .fontSize(14)
  .fontColor($r('app.color.light_gray'))
}

@Component
export default struct RecordList {

  @Consume @Watch('handleRecordsChange') records: RecordVO[]
  @State groups: GroupInfo<RecordType, RecordVO>[] = []

  handleRecordsChange(){
    this.groups = RecordService.calculateGroupInfo(this.records)
  }


  build() {
    List({space: CommonConstants.SPACE_10}){
      ForEach(this.groups, (group: GroupInfo<RecordType, RecordVO>) => {
        ListItem(){
          Column({space: CommonConstants.SPACE_8}){
            // 1.分组的标题
            Row({space: CommonConstants.SPACE_4}){
              Image(group.type.icon).width(24)
              Text(group.type.name).fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_700)
              Text(`建议${group.type.min}~${group.type.max}千卡`).grayText()
              Blank()
              Text(group.calorie.toFixed(0)).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%')
            .onClick(() => {
              router.pushUrl({
                url: 'pages/ItemIndex',
                params: {type: group.type}
              })
            })
            // 2.组内记录列表
            List(){
              ForEach(group.items, (item: RecordVO) => {
                ListItem(){
                  Row({space: CommonConstants.SPACE_6}){
                    Image(item.recordItem.image).width(50)
                    Column({space: CommonConstants.SPACE_4}){
                      Text(item.recordItem.name).fontWeight(CommonConstants.FONT_WEIGHT_500)
                      Text(`${item.amount}${item.recordItem.unit}`).grayText()
                    }
                    Blank()
                    Text(`${item.calorie.toFixed(0)}千卡`).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)
      .fillColor(Color.Red)
      .margin(5)
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值