鸿蒙开发日记:撸了个单位换算器,顺便把温度换算搞明白了
2026/6/4 9:18:17 网站建设 项目流程

鸿蒙开发日记:撸了个单位换算器,顺便把温度换算搞明白了

缘起

上周末做饭,菜谱上写的是"盎司",我家厨房秤只显示"克"。掏出手机现找换算工具,心想:这玩意儿我也能做啊!

正好最近在学鸿蒙开发,就当练手项目了。说干就干,一个周末下来,还真让我给撸出来了。


第一天:想清楚要做什么

功能定位

既然是练手,功能不用太复杂,但也得有实用性。我决定支持四大类:

  • 长度:mm、cm、m、km、英寸、英尺
  • 重量:mg、g、kg、吨、磅、盎司
  • 温度:摄氏、华氏、开尔文
  • 面积:mm²、cm²、m²、km²、公顷、英亩

这些基本覆盖日常需求了。

UI 怎么设计?

参考了几个主流换算App,发现大家都用类似的布局:

  1. 顶部类别切换(Tab)
  2. 输入区域(选单位 + 输数值)
  3. 输出区域(选单位 + 看结果)
  4. 交换按钮

简洁明了,我也这么干。


第二天:数据结构,磨刀不误砍柴工

单位怎么定义?

每个单位需要两个属性:名字和换算系数。

interfaceUnitInfo{label:string// 显示名称toBase:number// 换算到基准单位的系数}

系数是啥意思?

比如长度,我选"米"作为基准:

  • 1厘米 = 0.01米,所以toBase = 0.01
  • 1千米 = 1000米,所以toBase = 1000
  • 1英寸 = 0.0254米(对,就是这么精确)

这样换算起来就简单了:

输入值 × toBase = 基准单位值 基准单位值 ÷ 目标toBase = 结果

类别怎么定义?

interfaceCategory{name:stringicon:stringunits:UnitInfo[]convertFn?:(val,fromIdx,toIdx)=>number// 特殊换算函数}

为什么需要convertFn

因为温度不一样!摄氏、华氏、开尔文之间不是简单的乘除关系,需要特殊公式。这个后面细说。

完整数据

constCATEGORIES=[{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},]},// ... 其他类别]

第三天:换算算法,基准单位法

普通单位的换算

思路很简单:先把输入转成基准单位,再转成目标单位。

convert():void{constnum=parseFloat(this.inputValue)constcat=CATEGORIES[this.activeCategory]// 输入值 → 基准单位constbase=num*cat.units[this.fromIdx].toBase// 基准单位 → 目标单位constresult=base/cat.units[this.toIdx].toBasethis.resultValue=this.formatResult(result)}

举个例子:5千米换算成米

  1. 输入:5
  2. 转基准:5 × 1000 = 5000
  3. 转目标:5000 ÷ 1 = 5000

完美!

第一个坑:温度不是这么算的

我一开始把温度也用系数法定义了,结果测试时发现:100°C 换算成华氏度,结果是 100,而不是 212。

问题在哪?

温度换算不是线性的!摄氏度和华氏度的关系是:

  • °C → °F:C × 9/5 + 32
  • °F → °C:(F - 32) × 5/9

开尔文也是一样:

  • °C → K:C + 273.15
  • K → °C:K - 273.15

解决方案

写个特殊函数,先转成摄氏度,再转成目标单位:

convertFn:(val,fromIdx,toIdx)=>{// 第一步:转摄氏度letcelsiusif(fromIdx===0)celsius=val// 摄氏度elseif(fromIdx===1)celsius=(val-32)*5/9// 华氏度elsecelsius=val-273.15// 开尔文// 第二步:转目标if(toIdx===0)returncelsiusif(toIdx===1)returncelsius*9/5+32returncelsius+273.15}

第四天:UI 布局,卡片式设计

整体结构

Column ├── 标题 ├── 类别Tab(Row + ForEach) ├── 换算卡片(Column) │ ├── 输入区 │ ├── 交换按钮 │ └── 输出区 └── 底部提示

类别Tab

ForEach遍历类别数组,每个Tab是一个Column

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)})})}.backgroundColor('#F0F2F5').borderRadius(12)

选中状态:浅蓝背景 + 蓝字;未选中:透明背景 + 灰字。

单位选择器

这个有点意思,我做成了横向滚动的标签:

Scroll(){Row(){ForEach(cat.units,(unit,idx)=>{Text(unit.label).backgroundColor(this.fromIdx===idx?'#5B8DEF':'#F0F2F5').onClick(()=>{this.fromIdx=idxthis.convert()})})}}.scrollable(ScrollDirection.Horizontal)

选中的单位:蓝色背景 + 白字;未选中:灰色背景。

输入框

TextInput({placeholder:'输入数值',text:this.inputValue}).type(InputType.Number).onChange((val)=>{this.inputValue=valthis.convert()// 实时换算!})

亮点:每次输入都触发换算,不需要点"计算"按钮,输入即出结果。

交换按钮

中间放个按钮,点击互换源和目标单位:

Button(){Text('⇅').fontSize(24)}.onClick(()=>{consttmp=this.fromIdxthis.fromIdx=this.toIdxthis.toIdx=tmpthis.convert()})

第五天:细节优化,处理边界情况

第二个坑:浮点数精度

测试时输入0.1 + 0.2,结果显示0.30000000000000004

原因:JavaScript 的经典浮点数精度问题。

解决:用toFixed限制精度,再去掉多余的零:

formatResult(val){constfixed=val.toFixed(6)returnparseFloat(fixed).toString()}

第三个坑:单位索引越界

在长度类别选了第6个单位(英尺),切到温度类别时崩溃了。

原因:温度只有3个单位,索引5越界了。

解决:切换类别时重置索引:

switchCategory(idx){this.activeCategory=idxthis.fromIdx=0this.toIdx=Math.min(1,CATEGORIES[idx].units.length-1)}

极大极小数字

比如1毫米 = 0.000001千米,这显示起来太长了。

解决:科学计数法:

if(Math.abs(val)<0.000001&&val!==0){returnval.toExponential(4)}if(Math.abs(val)>999999999){returnval.toExponential(4)}

第六天:测试,各种边界情况


总结

学到了什么

  1. 数据结构很重要:好的数据结构能让算法简单很多
  2. 边界情况要考虑:越界、精度、极大极小值
  3. 实时交互体验好:输入即出结果,比点按钮爽

踩坑总结

表现解决
温度换算结果错误特殊函数处理
浮点精度小数位太长toFixed + parseFloat
索引越界切换类别崩溃重置索引 + Math.min
极大极小显示太长科学计数法

代码量

最后统计了一下,主文件Index.ets约 200 行。对于一个完整的应用来说,这个量级很合适——既不复杂,又有技术含量。


开发时间:2天
核心代码:~200行

做菜时再也不用到处找换算工具了,挺有成就感的!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询