HarmonyOS ArkTS 实战:实现一个BMI健康计算器

项目效果

本文实现的是一个基于 HarmonyOS 和 ArkTS 的BMI健康计算器。项目中使用 ArkUI 组件完成页面布局,通过 @State 管理状态数据,实现身高体重输入、性别选择、实时BMI计算、健康状态评估、健康建议和理想体重范围等功能。

最终运行效果如下:
在这里插入图片描述

页面主要包含以下内容:

  • 顶部应用标题;
  • 性别选择(男/女);
  • 身高输入(厘米);
  • 体重输入(公斤);
  • BMI结果大字体显示;
  • 健康状态颜色指示;
  • 健康建议文字;
  • 理想体重范围;
  • BMI范围参考说明;
  • 页面整体采用 ArkUI 声明式布局。

本文重点是演示如何在 HarmonyOS 项目中使用 ArkTS 和 ArkUI 实现一个健康计算类单页面应用。项目代码主要写在 entry/src/main/ets/pages/Index.ets 文件中,适合作为 HarmonyOS ArkTS 入门到进阶之间的练习案例。

前言

身体质量指数(BMI)是国际通用的衡量人体胖瘦程度以及是否健康的标准。通过BMI可以快速了解自己的体重是否在健康范围内,对于健康管理非常有帮助。手动计算BMI很简单,但一个好的计算器还能给出健康状态评估和建议。

从应用开发角度来看,这个项目不依赖后端接口,也不需要数据库,但能练习 ArkTS 中的状态管理、性别选择、数字输入、实时计算、条件判断和结果展示等内容。

本文基于 HarmonyOS 和 ArkTS 实现一个BMI健康计算器。用户可以输入性别、身高和体重,实时计算BMI值,查看健康状态,获取健康建议和理想体重范围。

这个项目的核心不是简单的BMI公式,而是完整的健康评估和友好的用户体验。每一次输入变化,都会实时重新计算并给出反馈,这正是 ArkUI 声明式开发的基本思想。

一、项目目标

本次实践主要实现以下目标:

  • 创建 HarmonyOS ArkTS 页面;
  • 使用 @Entry@Component 定义页面组件;
  • 使用 @State 管理页面状态;
  • 实现男女性别选择;
  • 身高和体重数字输入;
  • 实时计算BMI值;
  • 根据BMI判断健康状态;
  • 不同状态显示不同颜色;
  • 给出针对性健康建议;
  • 计算理想体重范围;
  • 显示BMI参考范围说明;
  • 使用 @Builder 封装性别选择按钮和结果卡片;
  • 完成一个可以运行的BMI计算器页面。

这个项目虽然是单页面应用,但它实现了完整的BMI计算和健康评估功能,比普通静态页面更适合练习 ArkTS。

二、技术栈

类型 内容
开发方向 HarmonyOS 应用开发
开发语言 ArkTS
UI 框架 ArkUI
SDK 版本 HarmonyOS API 23 及以上
工程模型 Stage 模型
核心组件 Text / TextInput / Button / Column / Row / Slider
状态管理 @State
数据处理 BMI计算 / 健康评估 / 实时响应
项目入口 entry/src/main/ets/pages/Index.ets
运行平台 模拟器或真机

本项目是 HarmonyOS 原生 ArkTS 项目。页面主体由 ArkUI 组件构建,核心逻辑写在 Index.ets 文件中,不依赖后端接口,也不需要额外配置数据库。

三、为什么选择BMI计算器项目

BMI健康计算器适合作为 ArkTS 练习项目,主要有以下几个原因。

第一,实用性强。BMI计算是健康管理的基础,很多人都会用到。

第二,业务场景真实。关注健康的人都需要了解自己的BMI,是非常实用的健康工具。

第三,适合练习状态管理。性别、身高、体重都属于页面状态。状态变化后,页面会自动刷新。

第四,适合练习条件判断。根据BMI值区间判断健康状态,给出不同建议和颜色。

第五,适合练习实时计算。输入变化时立即计算结果,提供流畅体验。

第六,扩展空间比较大。基础功能完成后,可以继续增加体脂率计算、基础代谢计算、卡路里计算、历史记录和健康趋势图。

在本项目中,身高体重和性别是核心数据。所有计算结果和健康评估都基于这些数据。

四、功能规则说明

BMI计算公式:BMI = 体重(kg) / (身高(m))²

中国成年人BMI标准:

BMI范围 状态 颜色
< 18.5 偏瘦 #3B82F6(蓝色)
18.5 - 23.9 正常 #10B981(绿色)
24 - 27.9 偏胖 #F59E0B(橙色)
≥ 28 肥胖 #EF4444(红色)

理想体重范围:18.5 ≤ BMI ≤ 23.9 对应的体重范围。

默认参数:

  • 性别:男
  • 身高:170cm
  • 体重:65kg

五、项目结构

本项目主要修改首页文件:

entry
└── src
    └── main
        └── ets
            └── pages
                └── Index.ets

其中:

文件 作用
Index.ets 编写页面结构、状态数据和BMI计算逻辑

本文不涉及复杂路由,也不需要额外创建多个页面。对于练习项目来说,把主要逻辑集中在一个 Index.ets 文件中更方便理解。

六、核心实现思路

本项目的核心流程如下:

  1. 使用 @State 保存性别、身高、体重;
  2. 用户选择性别;
  3. 用户输入身高(厘米);
  4. 用户输入体重(公斤);
  5. 参数变化时自动计算BMI;
  6. 根据BMI值判断健康状态;
  7. 根据状态显示对应颜色和建议;
  8. 计算理想体重范围;
  9. 显示BMI参考标准说明。

项目中最重要的状态变量如下:

@State gender: number = 0
@State height: string = '170'
@State weight: string = '65'

其中:

状态变量 作用
gender 性别(0=男,1=女)
height 身高(厘米)
weight 体重(公斤)

这些参数是计算的基础,任何一个变化都会触发重新计算。

七、Index.ets 完整代码

打开文件:

entry/src/main/ets/pages/Index.ets

将其中内容替换为下面代码:

@Entry
@Component
struct Index {
  @State gender: number = 0
  @State height: string = '170'
  @State weight: string = '65'

  private getBMI(): number {
    let h = parseFloat(this.height)
    let w = parseFloat(this.weight)
    if (isNaN(h) || isNaN(w) || h <= 0 || w <= 0) return 0
    
    let heightM = h / 100
    return w / (heightM * heightM)
  }

  private getBMIStatus(): { status: string, color: string, advice: string } {
    let bmi = this.getBMI()
    if (bmi === 0) return { status: '请输入数据', color: '#9CA3AF', advice: '请输入有效的身高和体重' }
    if (bmi < 18.5) {
      return {
        status: '偏瘦',
        color: '#3B82F6',
        advice: '您的体重偏轻,建议适当增加营养,均衡饮食,加强锻炼增强体质。'
      }
    }
    if (bmi < 24) {
      return {
        status: '正常',
        color: '#10B981',
        advice: '恭喜!您的体重在正常范围内,请继续保持健康的生活习惯。'
      }
    }
    if (bmi < 28) {
      return {
        status: '偏胖',
        color: '#F59E0B',
        advice: '您的体重略微超标,建议控制饮食,减少高热量食物摄入,增加运动量。'
      }
    }
    return {
      status: '肥胖',
      color: '#EF4444',
      advice: '您的体重已达到肥胖标准,建议制定科学的减重计划,必要时咨询医生。'
    }
  }

  private getIdealWeightRange(): string {
    let h = parseFloat(this.height)
    if (isNaN(h) || h <= 0) return '-'
    
    let heightM = h / 100
    let minWeight = 18.5 * heightM * heightM
    let maxWeight = 23.9 * heightM * heightM
    return `${minWeight.toFixed(1)} - ${maxWeight.toFixed(1)} kg`
  }

  @Builder
  GenderButton(text: string, icon: string, index: number) {
    Column() {
      Text(icon)
        .fontSize(40)
        .margin({ bottom: 8 })
      Text(text)
        .fontSize(16)
        .fontColor(this.gender === index ? Color.White : '#4B5563')
    }
    .width('48%')
    .height(100)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(this.gender === index ? '#0A59F7' : '#F3F4F6')
    .borderRadius(16)
    .onClick(() => {
      this.gender = index
    })
  }

  @Builder
  BMIRangeItem(range: string, status: string, color: string) {
    Row() {
      Circle()
        .width(12)
        .height(12)
        .fill(color)
      Text(range)
        .fontSize(14)
        .fontColor('#182431')
        .margin({ left: 8 })
      Blank()
      Text(status)
        .fontSize(14)
        .fontColor(color)
    }
    .width('100%')
    .padding({ top: 8, bottom: 8 })
  }

  build() {
    Column() {
      Text('BMI计算器')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#182431')
        .margin({ top: 22 })
      Text('基于 HarmonyOS ArkTS 的健康评估工具')
        .fontSize(14)
        .fontColor('#6B7280')
        .margin({ top: 8, bottom: 20 })

      Column() {
        Text('选择性别')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .fontColor('#182431')
          .width('100%')
          .margin({ bottom: 12 })
        
        Row() {
          this.GenderButton('男生', '👨', 0)
          Blank().width(16)
          this.GenderButton('女生', '👩', 1)
        }
        .width('100%')
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .margin({ bottom: 16 })
      .shadow({
        radius: 8,
        color: '#08000000',
        offsetX: 0,
        offsetY: 2
      })

      Column() {
        Text('身高体重')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .fontColor('#182431')
          .width('100%')
          .margin({ bottom: 12 })
        
        Row() {
          Text('身高')
            .fontSize(15)
            .fontColor('#182431')
            .width(60)
          TextInput({
            text: this.height,
            placeholder: '身高'
          })
            .layoutWeight(1)
            .height(44)
            .fontSize(16)
            .textAlign(TextAlign.End)
            .backgroundColor('#F5F7FA')
            .borderRadius(12)
            .padding({ left: 12, right: 12 })
            .type(InputType.Number)
            .onChange((value: string) => {
              this.height = value
            })
          Text('cm')
            .fontSize(15)
            .fontColor('#6B7280')
            .margin({ left: 8 })
        }
        .width('100%')
        .margin({ bottom: 12 })

        Row() {
          Text('体重')
            .fontSize(15)
            .fontColor('#182431')
            .width(60)
          TextInput({
            text: this.weight,
            placeholder: '体重'
          })
            .layoutWeight(1)
            .height(44)
            .fontSize(16)
            .textAlign(TextAlign.End)
            .backgroundColor('#F5F7FA')
            .borderRadius(12)
            .padding({ left: 12, right: 12 })
            .type(InputType.Number)
            .onChange((value: string) => {
              this.weight = value
            })
          Text('kg')
            .fontSize(15)
            .fontColor('#6B7280')
            .margin({ left: 8 })
        }
        .width('100%')
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .margin({ bottom: 16 })
      .shadow({
        radius: 8,
        color: '#08000000',
        offsetX: 0,
        offsetY: 2
      })

      Column() {
        Text('您的BMI')
          .fontSize(14)
          .fontColor('#6B7280')
          .margin({ bottom: 8 })
        
        Text(this.getBMI().toFixed(1))
          .fontSize(56)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.getBMIStatus().color)
        
        Text(this.getBMIStatus().status)
          .fontSize(20)
          .fontWeight(FontWeight.Medium)
          .fontColor(this.getBMIStatus().color)
          .margin({ top: 4 })
        
        Text(this.getBMIStatus().advice)
          .fontSize(14)
          .fontColor('#6B7280')
          .textAlign(TextAlign.Center)
          .lineHeight(22)
          .margin({ top: 12 })
          .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding(24)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .margin({ bottom: 16 })
      .shadow({
        radius: 8,
        color: '#08000000',
        offsetX: 0,
        offsetY: 2
      })

      Column() {
        Row() {
          Text('理想体重范围')
            .fontSize(14)
            .fontColor('#6B7280')
          Blank()
          Text(this.getIdealWeightRange())
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .fontColor('#10B981')
        }
        .width('100%')
        .padding({ bottom: 12 })
        .border({ width: { bottom: 1 }, color: '#F3F4F6' })

        Text('BMI参考标准(中国成年人)')
          .fontSize(13)
          .fontColor('#9CA3AF')
          .width('100%')
          .margin({ top: 12, bottom: 8 })
        
        this.BMIRangeItem('< 18.5', '偏瘦', '#3B82F6')
        this.BMIRangeItem('18.5 - 23.9', '正常', '#10B981')
        this.BMIRangeItem('24.0 - 27.9', '偏胖', '#F59E0B')
        this.BMIRangeItem('≥ 28.0', '肥胖', '#EF4444')
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({
        radius: 8,
        color: '#08000000',
        offsetX: 0,
        offsetY: 2
      })

      Text('* 本工具仅供参考,不能替代专业医疗建议')
        .fontSize(12)
        .fontColor('#9CA3AF')
        .margin({ top: 16 })
    }
    .width('100%')
    .height('100%')
    .padding({ left: 18, right: 18 })
    .backgroundColor('#F5F7FA')
  }
}

八、代码实现说明

1. BMI计算

使用标准BMI公式计算:体重(kg)除以身高(m)的平方:

let heightM = h / 100
return w / (heightM * heightM)
2. 健康状态评估

根据中国成年人BMI标准判断健康状态,返回状态文字、颜色和建议:

  • <18.5:偏瘦,蓝色,建议增加营养
  • 18.5-23.9:正常,绿色,建议保持
  • 24-27.9:偏胖,橙色,建议控制饮食
  • ≥28:肥胖,红色,建议减重
3. 理想体重范围

根据身高计算BMI在18.5-23.9之间对应的体重范围,给用户参考。

4. 性别选择

使用大按钮选择男女性别,选中状态用蓝色背景白色文字,直观清晰。

5. 实时计算

身高或体重变化时立即重新计算BMI和所有结果,不需要点击按钮。

6. 颜色反馈

不同健康状态使用不同颜色显示结果,直观醒目:正常绿色、偏胖橙色、肥胖红色、偏瘦蓝色。

九、运行项目

代码编写完成后,在DevEco Studio中运行项目。测试不同身高体重输入,验证BMI计算是否正确,检查健康状态判断和建议是否准确,理想体重范围是否正确。

十、开发中遇到的问题

  • 单位转换:身高输入是厘米,计算时要转换成米
  • 输入验证:处理空输入和非法输入,避免NaN
  • 中国标准:使用中国成年人BMI标准,而不是国际标准
  • 建议个性化:根据不同状态给出针对性的健康建议

十一、总结

本文基于HarmonyOS和ArkTS实现了一个BMI健康计算器。项目通过@State管理输入参数,使用标准BMI公式计算,实现了实时健康评估、状态颜色指示和个性化建议。这个项目展示了ArkTS中表单输入、条件判断和实时计算的基本方法,适合作为入门练习项目。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐