鸿蒙项目—黑马健康应用(2)

目录

一、饮食记录页面

1、首页Tabs UI设计

饮食记录页面Tabs结构:

TabContent结构:

关键技术:Tabs组件

运行效果:

代码如下:

2、顶部搜索栏

搜索栏结构

运行效果:

代码如下:

3、统计卡片

 统计卡片结构

关键技术:

日期选择器DatePicker

运行效果:

代码如下:

Swiper组件

运行效果:

代码如下:

4、记录列表

记录列表结构

关键技术:List组件

运行效果:

代码如下:

二、遇到问题

三、总结


一、饮食记录页面

1、首页Tabs UI设计

饮食记录页面Tabs结构:

TabContent结构:

饮食记录页面逻辑结构:

关键技术:Tabs组件

Tabs组件可以实现页面内视图内容快速切换,包含TabBar和TabContent两个部分,其基本语法如下:

Tabs({barPosition:BarPosition.End}) {//导航栏位置下部
      TabContent(){
        Text('记录内容')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_record'),$r('app.media.ic_calendar'),0))

      TabContent(){
        Text('发现内容')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_user'),$r('app.media.ic_user_portrait'),1))

      TabContent(){
        Text('我的内容')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_discover'),$r('app.media.discover'),2))
    }
    .width('100%')
    .height('100%')
    .vertical(false)//导航项水平排列
    .onChangeI(index => this.currentIndex = index)
}

其中TabBarBuilder为全局自定义构建函数,用来构建自定义组件,组件内的函数和变量的调用需加this

@state currentIndex: number = 0//导航角标

@Builder TabBuilder(title: string, image: ResourceStr, index: number){
    Column({ space: 4 }) {
        Image(image).fillcolor(this.currentIndex === index?Color.Blue:Color.Gray)//根据角标填充颜色,选中为蓝色否则为灰色
        Text(title) .fillcolor(this.currentIndex === index?Color.Blue:Color.Gray)
        justifyContent(FlexAlign.Center) // 设置内容在主轴上的对齐方式为居中
    }
}

运行效果:

点击切换底部导航项时,颜色也相应改变

代码如下:

import { CommonConstants } from '../common/constants/CommonConstants'
@Entry
@Component
struct Index {
  @State currentIndex: number = 0

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

  selectColor(index: number) {
    return this.currentIndex === index ? $r('app.color.primary_color') : $r('app.color.gray')
  }

  build() {
    Tabs({ barPosition: BarPosition.End }) {
      TabContent() {
        Text('饮食记录页面')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_record'), $r('app.media.ic_calendar'), 0))

      TabContent() {
        Text('发现页面')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_discover'), $r('app.media.discover'), 1))

      TabContent() {
        Text('我的主页')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_user'), $r('app.media.ic_user_portrait'), 2))

    }
    .width('100%')
    .height('100%')
    .onChange(index => this.currentIndex = index)

  }
}

2、顶部搜索栏

搜索栏结构

搜索框中可设置默认占位符

Search({placeholder: '搜索饮食或运动信息'})

Imgae图片为一个邮箱,邮箱右上角通过Badge组件添加未读信息数量

Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){
        Image($r('app.media.ic_public_email'))
          .width(24)
      }
//消息数量:1,位置:右上角

相关代码:

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

运行效果:

代码如下:

//Index.ets文件
TabContent() {
   RecordIndex()//调用记录首页
}

//RecordIndex.ets文件
import SearchHeader from './SearchHeader'
@Component
export default struct RecordIndex {


  build() {
    Column(){
      // 1.头部搜索栏
      SearchHeader()//调用搜索栏组件
      // 2.统计卡片
      Text('统计卡片')
      // 3.记录列表
      Text('记录列表')

    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.index_page_background'))
  }
}

//SearchHeader.ets
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)
  }
}

3、统计卡片

 统计卡片结构

要实现的功能:①日期选择窗口、②滑动统计卡片

关键技术:

日期选择器DatePicker
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)
        })
//start开始日期,end结束日期,selected设置选中项的日期
运行效果:

代码如下:
Swiper组件

滑块视图容器:提供子组件滑动轮播显示的能力。

运行效果:

代码如下:

CalorieStats.ets卡路里统计

import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct CalorieStats {
  intake: number=192
  expend: number=150
  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.ets营养素统计

import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NutrientStats {
   carbon: number=23
   protein: number=9
   fat: number=7

  recommendCarbon: number = CommonConstants.RECOMMEND_CARBON
  recommendProtein: number = CommonConstants.RECOMMEND_PROTEIN
  recommendFat: number = CommonConstants.RECOMMEND_FAT

  build() {
    Row({space: CommonConstants.SPACE_6}){
      this.StatsBuilder({
        label: '碳水化合物',
        value: this.carbon,
        recommend: this.recommendCarbon,
        color: $r('app.color.carbon_color')
      })
      this.StatsBuilder({
        label: '蛋白质',
        value: this.protein,
        recommend: this.recommendProtein,
        color: $r('app.color.protein_color')
      })
      this.StatsBuilder({
        label: '脂肪',
        value: this.fat,
        recommend: this.recommendFat,
        color: $r('app.color.fat_color')
      })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceEvenly)
    .padding({top: 30, bottom: 35})
  }

  @Builder StatsBuilder($$:{label: string, value: number, recommend: number, color: ResourceStr}){
    Column({space: CommonConstants.SPACE_6}){
      Stack(){
        Progress({
          value: $$.value,
          total: $$.recommend,
          type: ProgressType.Ring
        })
          .width(95)
          .style({strokeWidth: CommonConstants.DEFAULT_6})
          .color($$.color)
        Column({space: CommonConstants.SPACE_6}){
          Text('摄入推荐')
            .fontSize(12)
            .fontColor($r('app.color.gray'))
          Text(`${$$.value.toFixed(0)}/${$$.recommend.toFixed(0)}`)
            .fontSize(18)
            .fontWeight(CommonConstants.FONT_WEIGHT_600)
        }
      }
      Text(`${$$.label}(克)`)
        .fontSize(12)
        .fontColor($r('app.color.light_gray'))
    }
  }
}

4、记录列表

记录列表结构

关键技术:List组件

运行效果:

代码如下:

import router from '@ohos.router'
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,5],(item)=>{
        ListItem(){
          Column(){
            //1.分组标题
            Row({space:CommonConstants.SPACE_4}){
              Image($r('app.media.ic_breakfast')).width(24)
              Text('早餐').fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_700)
              Text('建议423-592千卡').grayText()
              Blank()
              Text('190').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%')
            //2.组内记录列表
            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('1片').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)
    .margin({top:10})
    .height('100%')
  }

  @Builder deleteButton(){
    Image($r('app.media.ic_public_delete_filled'))
      .width(20)
      .fillColor(Color.Red)
      .margin(5)
  }
}

二、遇到问题

        问题:如何实现切换TabBar选项时改变选中项目颜色

        解决方法:

  1. 定义状态变量:首先,定义一个角标状态变量来存储当前选中的标签索引。
  2. 修改 TabBarBuilder:更新 TabBarBuilder 方法,使其能够根据当前选中的索引来改变颜色。
  3. 处理 TabBar 切换事件:在 Tabs 组件中,使用 .onChange 方法来监听标签页的切换事件,并更新当前选中的索引。
  4. 确保 TabBar 更新:当当前选中的索引发生变化时,确保 TabBar 能够响应这一变化并重新渲染。

三、总结

        本次开发的饮食记录页面提供了用户友好的界面和流畅的交互体验。通过Tabs组件、搜索栏、统计卡片和记录列表,用户能够轻松地记录和管理自己的饮食情况。其中新用到了Tabs组件切换用户点击内容,无需页面路由,更加便捷,DatePicker组件生成选择日期弹窗。Swiper组件,切换记录卡片信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值