HarmonyOS NEXT 单位换算器开发:完整技术指南

前言

单位换算器是一个经典的工具类应用,涵盖了数据结构设计、算法实现、状态管理、UI交互等多个技术点。本文将从零开始,详细讲解如何使用 HarmonyOS NEXT 开发一个功能完整的单位换算器。

项目信息

  • 包名:com.example.myapplication
  • API版本:HarmonyOS NEXT (API 23)

一、需求分析

1.1 功能需求

功能 描述
多类别支持 长度、重量、温度、面积
实时换算 输入即出结果
单位选择 横向滚动选择器
单位交换 一键互换源/目标单位

1.2 单位定义

长度单位(基准:米)
单位 符号 换算系数
毫米 mm 0.001
厘米 cm 0.01
m 1
千米 km 1000
英寸 in 0.0254
英尺 ft 0.3048
重量单位(基准:克)
单位 符号 换算系数
毫克 mg 0.001
g 1
千克 kg 1000
t 1000000
lb 453.59237
盎司 oz 28.349523125
温度单位
单位 符号 换算方式
摄氏度 °C 基准
华氏度 °F 特殊公式
开尔文 K 特殊公式
面积单位(基准:平方米)
单位 符号 换算系数
平方毫米 mm² 0.000001
平方厘米 cm² 0.0001
平方米 1
平方千米 km² 1000000
公顷 ha 10000
英亩 acre 4046.8564224

二、数据结构设计

2.1 单位信息接口

interface UnitInfo {
  label: string      // 显示标签
  toBase: number     // 换算到基准单位的系数
}

2.2 类别接口

interface Category {
  name: string       // 类别名称
  icon: string       // 图标
  units: UnitInfo[]  // 单位列表
  convertFn?: (val: number, fromIdx: number, toIdx: number) => number
}

2.3 数据定义

const CATEGORIES: Category[] = [
  {
    name: '长度', icon: '📏',
    units: [
      { label: '毫米 (mm)', toBase: 0.001 },
      { label: '厘米 (cm)', toBase: 0.01 },
      { label: '米 (m)', toBase: 1 },
      { label: '千米 (km)', toBase: 1000 },
      { label: '英寸 (in)', toBase: 0.0254 },
      { label: '英尺 (ft)', toBase: 0.3048 },
    ]
  },
  {
    name: '重量', icon: '⚖️',
    units: [
      { label: '毫克 (mg)', toBase: 0.001 },
      { label: '克 (g)', toBase: 1 },
      { label: '千克 (kg)', toBase: 1000 },
      { label: '吨 (t)', toBase: 1_000_000 },
      { label: '磅 (lb)', toBase: 453.59237 },
      { label: '盎司 (oz)', toBase: 28.349523125 },
    ]
  },
  {
    name: '温度', icon: '🌡️',
    units: [
      { label: '摄氏度 (°C)', toBase: 0 },
      { label: '华氏度 (°F)', toBase: 0 },
      { label: '开尔文 (K)', toBase: 0 },
    ],
    convertFn: (val, fromIdx, toIdx) => {
      // 温度特殊换算
      let celsius: number
      if (fromIdx === 0) celsius = val
      else if (fromIdx === 1) celsius = (val - 32) * 5 / 9
      else celsius = val - 273.15

      if (toIdx === 0) return celsius
      if (toIdx === 1) return celsius * 9 / 5 + 32
      return celsius + 273.15
    }
  },
  {
    name: '面积', icon: '🔲',
    units: [
      { label: '平方毫米 (mm²)', toBase: 0.000001 },
      { label: '平方厘米 (cm²)', toBase: 0.0001 },
      { label: '平方米 (m²)', toBase: 1 },
      { label: '平方千米 (km²)', toBase: 1_000_000 },
      { label: '公顷 (ha)', toBase: 10_000 },
      { label: '英亩 (acre)', toBase: 4046.8564224 },
    ]
  },
]

三、换算算法

3.1 基准单位法

对于线性单位(长度、重量、面积),使用基准单位法:

输入值 × toBase[源] = 基准值
基准值 ÷ toBase[目标] = 结果

3.2 代码实现

convert(): void {
  const trimmed = this.inputValue.trim()
  if (trimmed.length === 0) {
    this.resultValue = ''
    return
  }

  const num = parseFloat(trimmed)
  if (isNaN(num)) {
    this.resultValue = '—'
    return
  }

  const cat = CATEGORIES[this.activeCategory]

  if (cat.convertFn) {
    // 温度特殊换算
    const res = cat.convertFn(num, this.fromIdx, this.toIdx)
    this.resultValue = this.formatResult(res)
  } else {
    // 标准换算
    const base = num * cat.units[this.fromIdx].toBase
    const res = base / cat.units[this.toIdx].toBase
    this.resultValue = this.formatResult(res)
  }
}

3.3 结果格式化

formatResult(val: number): string {
  if (isNaN(val) || !isFinite(val)) return '—'
  
  // 极小数字
  if (Math.abs(val) < 0.000001 && val !== 0) {
    return val.toExponential(4)
  }
  
  // 极大数字
  if (Math.abs(val) > 999999999) {
    return val.toExponential(4)
  }
  
  // 正常数字:四舍五入去零
  const fixed = val.toFixed(6)
  return parseFloat(fixed).toString()
}

四、状态管理

4.1 状态定义

@Entry
@Component
struct UnitConverter {
  @State activeCategory: number = 0  // 当前类别
  @State fromIdx: number = 0         // 源单位索引
  @State toIdx: number = 1           // 目标单位索引
  @State inputValue: string = ''     // 输入值
  @State resultValue: string = ''    // 结果值
}

4.2 状态联动

// 切换类别
switchCategory(idx: number): void {
  this.activeCategory = idx
  this.fromIdx = 0
  this.toIdx = Math.min(1, CATEGORIES[idx].units.length - 1)
  this.convert()
}

// 交换单位
swapUnits(): void {
  const tmp = this.fromIdx
  this.fromIdx = this.toIdx
  this.toIdx = tmp
  this.convert()
}

五、UI布局

5.1 整体结构

build() {
  Column() {
    // 标题
    Text('📐 单位换算器')
      .fontSize(26)
      .fontWeight(FontWeight.Bold)

    // 类别Tab
    Row() {
      ForEach(CATEGORIES, (cat, idx) => {
        Column() {
          Text(cat.icon).fontSize(22)
          Text(cat.name).fontSize(13)
        }
        .backgroundColor(this.activeCategory === idx ? '#EEF3FD' : 'transparent')
        .onClick(() => this.switchCategory(idx))
      })
    }

    // 换算卡片
    Column() {
      // 输入区
      Column() { ... }
      
      // 交换按钮
      Column() { ... }
      
      // 输出区
      Column() { ... }
    }
  }
}

5.2 单位选择器

Scroll() {
  Row() {
    ForEach(cat.units, (unit, idx) => {
      Text(unit.label)
        .fontSize(14)
        .fontColor(this.fromIdx === idx ? '#FFFFFF' : '#555555')
        .padding({ top: 6, bottom: 6, left: 14, right: 14 })
        .backgroundColor(this.fromIdx === idx ? '#5B8DEF' : '#F0F2F5')
        .borderRadius(16)
        .margin({ right: 8 })
        .onClick(() => {
          this.fromIdx = idx
          this.convert()
        })
    })
  }
}
.scrollable(ScrollDirection.Horizontal)

5.3 输入框

TextInput({ placeholder: '输入数值', text: this.inputValue })
  .height(52)
  .width('100%')
  .type(InputType.Number)
  .backgroundColor('#F5F7FA')
  .borderRadius(12)
  .fontSize(22)
  .fontWeight(FontWeight.Bold)
  .onChange((val) => {
    this.inputValue = val
    this.convert()
  })

5.4 结果显示

if (this.inputValue.trim().length > 0 && this.resultValue.length > 0) {
  Row() {
    Text('= ').fontSize(28).fontColor('#5B8DEF')
    Text(this.resultValue).fontSize(32).fontWeight(FontWeight.Bold)
    Text(' ' + unitLabel).fontSize(16).fontColor('#888888')
  }
} else {
  Text('请输入数值').fontSize(16).fontColor('#CCCCCC')
}

六、运行效果

在这里插入图片描述

七、常见问题

问题1:索引越界

原因:不同类别的单位数量不同。

解决

this.toIdx = Math.min(1, CATEGORIES[idx].units.length - 1)

问题2:浮点精度

原因:JavaScript浮点数计算误差。

解决

const fixed = val.toFixed(6)
return parseFloat(fixed).toString()

问题3:温度换算错误

原因:温度不能使用系数法。

解决:使用 convertFn 实现特殊换算。


八、总结

技术要点

类别 知识点
数据结构 interface定义、数组结构
算法 基准单位法、温度特殊处理
状态管理 @State装饰器
UI组件 ForEach、Scroll、TextInput

项目亮点

  • ✅ 实时换算,输入即出结果
  • ✅ 智能格式化
  • ✅ 横向滚动选择器
  • ✅ 卡片式UI设计

完整代码见项目文件,有问题欢迎评论区讨论!

Logo

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

更多推荐