HarmonyOS NEXT ArkUI 实战 012|API20 实现汇率转换器,完整源码 + 踩坑指南 + 核心知识点详解
本篇基于 HarmonyOS NEXT API20 开发一款轻量化汇率换算 App,覆盖 8 种主流币种,实现实时换算、一键互换币种、汇率明细展示、非法输入拦截等全套业务。全文源码严格遵循 ArkTS 强类型规范,解决 API20 Select、对象字面量、边框属性、隐式返回等高频编译错误。文章从项目需求、落地代码、分层解析、排错方案、项目拓展五个维度讲解,适合鸿蒙入门开发者夯实Record数据映射、Select下拉、状态驱动UI、浮点数精度四大高频知识点,代码复制粘贴至 index.ets 即可编译运行。
关键词:HarmonyOS NEXT、ArkTS、API20、汇率转换器、ArkUI 实战
一、项目前言
1.1 项目开发背景
跨境出行、海淘购物、境外理财越来越普遍,日常频繁需要快速换算不同国家货币。市面上汇率工具大多捆绑广告、体积臃肿,基于鸿蒙原生开发轻量化汇率工具,启动快、占用资源少、适配全鸿蒙设备。
项目内部采用静态模拟基准汇率(美元为锚定货币),整体代码分层清晰、低耦合,开发者只需替换网络请求逻辑,对接公开汇率接口即可升级为商用实时汇率软件,是入门 ArkUI 业务开发的优质练手项目。
1.2 产品使用场景
出境旅游:快速换算当地物价,规划出行消费预算;
跨境网购:海淘时外币转本币,直观对比商品到手价格;
个人理财:持有多币种资产用户日常记账换算;
外贸办公:商务对接快速报价,提升外币核算效率。
1.3 应用功能清单
支持 USD/CNY/EUR/JPY/GBP/KRW/HKD/TWD 八大主流币种;
输入金额实时自动换算,无需额外点击计算按钮;
⇄互换按钮一键切换源货币、目标货币,自动刷新结果;
结果卡片展示换算金额 + 实时兑换比例;
首页快捷展示四大常用币种基准汇率;
输入容错:空字符、负数、英文乱码自动捕获并文字提示。
1.4 开发环境与技术栈
开发 IDE:DevEco Studio
系统版本:HarmonyOS NEXT、API Level 20
语言:ArkTS
UI 框架:ArkUI 声明式 UI
核心组件:Column、Row、TextInput、Select、Button、ForEach、Blank
1.5 UI 整体设计
整体采用金融绿色风格#059669搭配灰白卡片,页面从上至下五层结构:标题→金额输入区→币种选择区→换算结果卡片→常用汇率列表,布局紧凑、符合移动端单手操作逻辑。
二、核心技术知识点详解
2.1 Record 泛型数据存储(本项目核心)
Record 是 ArkTS 用于键值对结构化存储的内置工具类型,键统一为字符串币种编码,值区分数字汇率 / 字符串符号 / 字符串中文名,编译阶段做类型校验,杜绝非法数据赋值。
ets
// 汇率表:基准美元=1
private readonly rateMap: Record<string, number> = {
“USD”: 1.0,“CNY”:7.24,“EUR”:0.92,“JPY”:149.50,“GBP”:0.79,“KRW”:1320.0,“HKD”:7.83,“TWD”:31.5
}
// 货币符号
private readonly symbolMap: Record<string, string> = {“USD”:“","CNY":"¥","EUR":"€","JPY":"¥","GBP":"£","KRW":"₩","HKD":"HK","CNY":"¥","EUR":"€","JPY":"¥","GBP":"£","KRW":"₩","HKD":"HK","CNY":"¥","EUR":"€","JPY":"¥","GBP":"£","KRW":"₩","HKD":"HK”,“TWD”:“NT$”}
// 中文名称
private readonly nameMap: Record<string, string> = {“USD”:“美元”,“CNY”:“人民币”,“EUR”:“欧元”,“JPY”:“日元”,“GBP”:“英镑”,“KRW”:“韩元”,“HKD”:“港币”,“TWD”:“新台币”}
Record 优势:类型安全、IDE 智能提示、统一管理数据源、后续新增币种仅需扩充对象字段。
2.2 API20 规范 Select 下拉选型
API20 严格禁止无类型对象字面量直接传入 Select,必须提前定义interface规范选项结构,再通过Object.keys+map动态生成下拉数组;同时 API20 移除 Select.fontSize/optionFont 属性,不能直接配置下拉字体。
2.3 @State 响应式状态
ArkUI 依靠@State修饰变量实现状态驱动视图,输入金额变更、币种切换、互换操作都会修改状态值,页面自动刷新对应 UI,不用手动刷新控件,是声明式 UI 的核心特性。
2.4 浮点数金融精度处理
JS/ArkTS 基于 IEEE754 浮点数计算,直接运算极易出现多位小数误差,金融场景统一使用toFixed(2)保留两位小数,同时前置parseFloat+isNaN做输入校验,拦截非法输入。
2.5 aboutToAppear 页面生命周期
组件挂载完成自动执行初始化换算,App 打开默认展示 1 美元兑人民币结果,优化用户初次打开体验。
三、index.ets 完整可运行源码(API20 零报错)
新建 entry 页面直接全量替换,无需导入额外依赖
// CurrencyConverter 汇率转换器 API20最终可用代码interfaceOptionItem{value:string}@Entry@Componentstruct Index{@StateinputAmount:string="1";@StatefromCurr:string="USD";@StatetoCurr:string="CNY";@StateconvertResult:string="";// 汇率、符号、名称三大数据源privatereadonlyrateMap:Record<string,number>={"USD":1.0,"CNY":7.24,"EUR":0.92,"JPY":149.50,"GBP":0.79,"KRW":1320.0,"HKD":7.83,"TWD":31.5}privatereadonlysymbolMap:Record<string,string>={"USD":"$","CNY":"¥","EUR":"€","JPY":"¥","GBP":"£","KRW":"₩","HKD":"HK$","TWD":"NT$"}privatereadonlynameMap:Record<string,string>={"USD":"美元","CNY":"人民币","EUR":"欧元","JPY":"日元","GBP":"英镑","KRW":"韩元","HKD":"港币","TWD":"新台币"}// 生成规范Select下拉选项privategetSelectOpts():OptionItem[]{returnObject.keys(this.rateMap).map((item:string):OptionItem=>{constopt:OptionItem={value:item}returnopt})}// 页面初始化aboutToAppear():void{this.calcConvert()}// 核心换算逻辑privatecalcConvert():void{constnum=parseFloat(this.inputAmount)// 非法字符校验if(isNaN(num)){this.convertResult="请输入有效数字金额"return}// 负数拦截if(num<=0){this.convertResult="金额不能小于等于0"return}constfromRate=this.rateMap[this.fromCurr]consttoRate=this.rateMap[this.toCurr]constusdBase=num/fromRateconsttargetNum=usdBase*toRatethis.convertResult=`${this.symbolMap[this.toCurr]}${targetNum.toFixed(2)}`}// 币种互换privateswapCurrency():void{consttemp=this.fromCurrthis.fromCurr=this.toCurrthis.toCurr=tempthis.calcConvert()}build(){Column(){// 顶部标题Text("汇率转换器").fontSize(26).fontWeight(FontWeight.Bold).fontColor("#1e293b").margin({top:24,bottom:20})// 金额输入区Column(){Text("输入需要换算的金额").fontSize(14).fontColor("#64748b").width("100%").margin({bottom:8})TextInput({text:this.inputAmount,placeholder:"仅填写数字"}).width("100%").height(52).fontSize(22).textAlign(TextAlign.Center).backgroundColor("#ffffff").borderRadius(12).border({width:1,color:"#e2e8f0"}).onChange((val:string)=>{this.inputAmount=valthis.calcConvert()})}.width("92%").margin({bottom:22})// 币种选择+互换按钮Row(){Column(){Text("从").fontSize(12).fontColor("#64748b").margin({bottom:4})Select(this.getSelectOpts()).value(this.fromCurr).width(112).height(42).onSelect((idx:number)=>{constarr=Object.keys(this.rateMap)this.fromCurr=arr[idx]this.calcConvert()})Text(this.nameMap[this.fromCurr]).fontSize(12).fontColor("#64748b").margin({top:4})}.alignItems(HorizontalAlign.Center)Button("⇄").width(56).height(56).fontSize(26).fontColor("#059669").backgroundColor("#fff").border({width:2,color:"#059669"}).borderRadius(28).onClick(()=>this.swapCurrency())Column(){Text("到").fontSize(12).fontColor("#64748b").margin({bottom:4})Select(this.getSelectOpts()).value(this.toCurr).width(112).height(42).onSelect((idx:number)=>{constarr=Object.keys(this.rateMap)this.toCurr=arr[idx]this.calcConvert()})Text(this.nameMap[this.toCurr]).fontSize(12).fontColor("#64748b").margin({top:4})}.alignItems(HorizontalAlign.Center)}.width("92%").justifyContent(FlexAlign.SpaceBetween).margin({bottom:24})// 换算结果卡片Column(){Text("换算结果").fontSize(14).fontColor("#64748b").width("100%").margin({bottom:10})Text(this.convertResult).fontSize(38).fontWeight(FontWeight.Bold).fontColor("#059669").margin({bottom:8})Text(`1${this.fromCurr}=${(this.rateMap[this.toCurr]/this.rateMap[this.fromCurr]).toFixed(4)}${this.toCurr}`).fontSize(13).fontColor("#64748b")}.width("92%").padding(22).backgroundColor("#ffffff").borderRadius(16).margin({bottom:20})// 常用汇率列表Column(){Text("常用币种基准汇率(兑USD)").fontSize(17).fontWeight(FontWeight.Bold).width("100%").margin({bottom:12})ForEach(["USD","EUR","JPY","GBP"],(curr:string)=>{Column(){Row(){Text(`${curr}(${this.nameMap[curr]})`).fontSize(14).fontColor("#1e293b")Blank()Text(`${this.symbolMap[curr]}${this.rateMap[curr].toFixed(2)}`).fontSize(14).fontColor("#059669")}.width("100%").padding({top:9,bottom:9})}.border({width:{bottom:1},color:"#eeeeee"})})}.width("92%").padding(20).backgroundColor("#ffffff").borderRadius(16)}.width("100%").height("100%").backgroundColor("#f6faf8").padding({left:8,right:8})}}四、代码分层拆解
4.1 数据层
三块 Record 全局静态数据,统一管理汇率、符号、中文名,集中维护。
4.2 工具方法层
getSelectOpts:格式化下拉选项,解决 API20 无类型对象报错;
calcConvert:核心换算 + 输入校验;
swapCurrency:币种互换后重算。
4.3 UI 视图层
自上而下五大布局模块,Column 垂直布局 + Row 水平布局嵌套,卡片圆角 + 白底区分功能区块。
五、API20 踩坑总结(博文加分重点)
arkts-no-untyped-obj-literals:不能裸写{value:“”},提前定义 interface OptionItem;
Select 无 fontSize/selectedFont:API20 废弃该属性,删除相关代码;
borderBottom 属性不存在:新版本取消单边框简写,改用border({width:{bottom:1}});
隐式返回报错:所有自定义函数标注返回类型():void。
六、项目拓展升级方案
接入网络实时汇率:使用 fetch 请求免费公开汇率 API,替换静态 rateMap;
历史记录:AppStorage 存储换算记录,新增历史页面;
深色模式:跟随系统配置,动态切换页面背景与字体色;
防抖优化:输入频繁触发计算增加防抖函数,减少无效运算。
七、文末总结
本项目覆盖鸿蒙 ArkUI 入门必备:数据映射、状态管理、组件交互、布局开发、异常校验五大核心,适配 API20 最新语法规范,规避新手高频编译错误。吃透本案例后,可举一反三开发单位换算、计算器、价格统计等同类工具类 App,夯实原生鸿蒙开发基本功。
