鸿蒙原生计算器应用开发实战 — 从零搭建到运行
一、前言
在前一篇文章中,我们详细介绍了 HarmonyOS 原生应用开发环境 的搭建过程,包括 DevEco Studio 安装、SDK 配置、模拟器创建和真机调试的全流程。不少读者反馈说虽然环境搭起来了,但不知道下一步该做什么,缺一个真正能上手练的项目。所以今天我们就在已经搭建好的环境基础上,从零开始开发一个完整的鸿蒙计算器应用。
为什么选择计算器?因为计算器是移动端最经典、最适合入门的练手项目。它麻雀虽小五脏俱全:
- 页面布局涉及 ArkUI 的 Column、Row、Text、Button 等多种组件
- 交互逻辑涵盖按钮点击、状态管理、事件分发
- 运算逻辑涉及数字拼接、小数点处理、运算符优先级
- 边缘情况处理包括除零错误、数值溢出、连续运算
一个计算器做下来,ArkUI 开发的核心知识就基本都过了一遍。本文将从项目创建开始,带你一步步实现一个功能完善、界面精美的深色主题计算器,所有代码都会详细拆解,每一行都能看懂。
二、项目准备
2.1 开发环境要求
在开始之前,请确保你的环境满足以下条件:
- IDE:DevEco Studio(推荐最新版本,可从华为开发者官网下载)
- SDK:API 23 或以上在 SDK Manager 中安装
- 模拟器或真机:建议先用模拟器调试,再上真机
- 语言框架:ArkTS + ArkUI(HarmonyOS 原生声明式开发栈)
如果还没有搭建环境,可以参考上一篇文章《HarmonyOS 原生应用开发环境搭建指南》完成配置。
2.2 新建项目
打开 DevEco Studio,按以下步骤创建项目:
- 点击 File → New → Create Project
- 选择 Empty Ability 模板(最基础的模板,没有任何示例代码干扰)
- 在弹出的配置页面填写项目信息:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| 项目名称 | Calculator | 全部英文,不要出现中文 |
| 包名 | com.example.calculator | 应用唯一标识,反向域名格式 |
| 保存路径 | 英文路径 | 不要放在有中文或空格的目录下 |
| Compile SDK | API 23 或以上 | 根据你安装的 SDK 版本选择 |
| 模块名称 | entry | 默认即可,这是应用入口模块 |
- 点击 Finish 完成创建
创建完成后,DevEco Studio 会开始自动同步项目配置,第一次同步可能需要等待几分钟,取决于网络速度和电脑配置。同步完成后可以看到完整的项目目录结构。
三、项目结构分析
新建的项目默认生成了完整的工程骨架,这是 HarmonyOS 应用的标准项目结构:
Calculator/
├── AppScope/
│ └── app.json5 ← 应用级配置(包名、版本号、API 等级)
├── entry/
│ ├── src/main/
│ │ ├── ets/
│ │ │ ├── entryability/ ← Ability 生命周期管理
│ │ │ │ └── EntryAbility.ts
│ │ │ └── pages/ ← 页面文件(⭐ 我们主要修改这里)
│ │ │ └── Index.ets ← 计算器主页面
│ │ ├── module.json5 ← 模块配置(注册 Ability、页面路由)
│ │ └── resources/ ← 资源文件(字符串、颜色、图标等)
│ └── oh-package.json5 ← 模块依赖配置
├── build-profile.json5 ← 构建配置
├── hvigorfile.ts ← Hvigor 构建脚本
└── oh-package.json5 ← 项目依赖配置
我们的计算器只需要修改一个文件—— entry/src/main/ets/pages/Index.ets。其他所有配置文件都由 IDE 自动管理,不需要手动改动,这也是使用 IDE 向导创建项目的最大好处:环境配置零成本,专注写代码。
四、计算器 UI 设计
4.1 整体布局
计算器的 UI 采用经典的上下结构,上方是结果显示区域,下方是按钮网格:
┌─────────────────────────┐
│ │
│ 123.45 │ ← 显示区域(深色面板,右对齐)
│ │
├─────────────────────────┤
│ C ± % ÷ │
│ 7 8 9 × │
│ 4 5 6 - │ ← 按钮网格(4列5行,圆角按钮)
│ 1 2 3 + │
│ 0 . = │
└─────────────────────────┘
布局采用 Column 纵向排列,显示区域占固定高度 160 像素,按钮区域使用 layoutWeight(1) 占满剩余空间,保证在不同屏幕尺寸下按钮区域都能自适应填充。
4.2 配色方案
为了更接近 iOS 计算器的视觉风格,采用了深色主题配色:
| 元素 | 颜色 | 色值 | 用途 |
|---|---|---|---|
| 整体背景 | 纯黑 | #000000 |
沉浸式深色体验 |
| 显示面板 | 深灰 | #1C1C1E |
与黑色背景形成层次感 |
| 数字键 | 深灰底白字 | #333333 / #FFFFFF |
主色调,清晰醒目 |
| 运算符键 | 橙色底白字 | #FF9F0A / #FFFFFF |
突出显示,快速定位 |
| 功能键 | 浅灰底黑字 | #A5A5A5 / #000000 |
与数字键区分开 |
| 0 键 | 宽按钮 | 160px(普通键2倍) | 符合真实计算器使用习惯 |
按钮统一采用 37.5 像素圆角(75 像素高度的一半),形成完美圆形按钮效果。
4.3 按钮数据模型
按钮文本使用二维数组定义,清晰直观,便于后期增删改:
private readonly buttons: string[][] = [
['C', '±', '%', '÷'], // 第一行:功能键 + 除号
['7', '8', '9', '×'], // 第二行:数字键 + 乘号
['4', '5', '6', '-'], // 第三行:数字键 + 减号
['1', '2', '3', '+'], // 第四行:数字键 + 加号
['0', '.', '', '='] // 第五行:宽0键 + 小数点 + 占位 + 等号
]
第五行第三列的空字符串 '' 用作占位符,在渲染时生成一个透明不可见的 Button 组件,目的是保持网格对齐——因为左侧 0 键占了两个按钮的宽度(160px),右侧等号占了第 4 列,第 3 列需要空出来才能让布局不乱。
五、核心代码实现
全部代码写在一个 Index.ets 文件中。下面按模块拆解讲解,你可以边看边对照手中的代码。
5.1 状态变量定义
@State displayText: string = '0'
private currentInput: string = ''
private previousInput: string = ''
private currentOperator: string = ''
private isNewInput: boolean = true
private hasError: boolean = false
private readonly MAX_DIGITS: number = 15
这里有一个重要的 ArkUI 知识点:@State 装饰器标记的变量是响应式的,只要它的值发生变化,UI 就会自动重新渲染。计算器中只有 displayText 需要直接驱动 UI,所以只有它被标记为 @State。
其余变量(currentInput、previousInput 等)是纯粹的内部逻辑状态,它们变化时不需要触发 UI 刷新——我们会在逻辑完成时通过更新 displayText 来间接触发渲染。这样的设计让状态管理更清晰,也避免了不必要的渲染开销。
5.2 事件分发机制
private onButtonClick(label: string): void {
if (this.hasError && label !== 'C') return // 错误状态锁
switch (label) {
case 'C': this.clearAll(); break
case '±': this.toggleSign(); break
case '%': this.percent(); break
case '÷': case '×': case '-': case '+': this.handleOperator(label); break
case '=': this.calculate(); break
case '.': this.appendDot(); break
default: this.appendNumber(label)
}
}
所有按钮共用同一个点击处理器,通过 switch 分发到不同的处理方法。这个设计的优点是:
- 代码简洁:不需要给每个按钮写单独的回调
- 扩展性好:新增按钮类型只需要加一个
case - 统一拦截:错误状态下在最前面做拦截,所有非 C 操作都被阻止
5.3 数字输入与格式化
数字输入的核心是字符串拼接,但需要处理几个边界情况:
private appendNumber(num: string): void {
if (this.isNewInput) {
this.currentInput = num // 新输入:替换当前值
this.isNewInput = false
} else {
if (this.currentInput.length >= this.MAX_DIGITS) return // 超长截断
this.currentInput += num // 连续输入:拼接数字
}
this.displayText = this.currentInput
}
关键处理逻辑:
- 新输入替换:当用户按完运算符或等号后,
isNewInput为true,此时按数字会替换掉当前显示内容,而不是拼接 - 长度限制:
MAX_DIGITS = 15,防止无限输入撑爆显示区域 - 小数点防重:
appendDot()方法中通过includes('.')检查当前数字是否已包含小数点 - 自动补零:小数点在新输入状态下按
.,会自动变为0.
5.4 运算核心算法
计算器的运算逻辑采用经典的双操作数模式:
private handleOperator(op: string): void {
if (this.currentOperator !== '' && !this.isNewInput) {
this.calculate() // 链式运算:先完成前一步再进入下一步
}
this.previousInput = this.currentInput || '0'
this.currentOperator = op
this.isNewInput = true
}
private calculate(): void {
if (this.currentOperator === '' || this.isNewInput) return
const prev = parseFloat(this.previousInput)
const curr = parseFloat(this.currentInput)
let result = 0
switch (this.currentOperator) {
case '+': result = prev + curr; break
case '-': result = prev - curr; break
case '×': result = prev * curr; break
case '÷':
if (curr === 0) { this.showError('Error'); return }
result = prev / curr; break
}
if (!isFinite(result)) { this.showError('Error'); return }
this.currentInput = this.formatNumber(result)
this.displayText = this.currentInput
this.currentOperator = ''
this.isNewInput = true
}
这个看似简单的逻辑有几个值得注意的设计点:
链式运算支持:当用户按下 5 + 3 + 2 = 时,流程是这样的——按 5 输入,按 + 存储操作数 5 和运算符 +,按 3 替换输入为 3,按 + 时检测到已有运算符,先计算 5+3=8,再存储 8 和新运算符 +,按 2 替换输入为 2,按 = 计算 8+2=10。这就是链式运算的实现原理。
除零保护:在数学中,任何数除以 0 都是未定义的。ArkTS 的浮点运算中 5 / 0 会返回 Infinity,这显然不是用户想要的结果。所以我们提前用 curr === 0 判断,显示友好的 Error 信息。
数值格式化:formatNumber() 方法处理了两种特殊情况——极小数(如 0.000000001)自动转为科学计数法,超长数字截断到 15 位。同时也通过 parseFloat(toPrecision(12)) 去掉了浮点运算带来的多余小数位(例如 0.1 + 0.2 在浮点运算中的结果是 0.30000000000000004,会被格式化为 0.3)。
5.5 UI 构建详解
ArkUI 的 UI 构建采用声明式语法,在 build() 方法中描述界面结构:
build() {
Column() {
// ── 显示区域 ──
Column() {
Text(this.displayText)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.textAlign(TextAlign.End)
.width('100%')
.padding({ right: 20 })
.maxLines(1)
}
.width('100%')
.height(160)
.backgroundColor('#1C1C1E')
.justifyContent(FlexAlign.End)
.padding({ bottom: 20 })
// ── 按钮区域 ──
Column() {
ForEach(this.buttons, (row: string[]) => {
Row() {
ForEach(row, (label: string) => {
if (label === '') {
Button().width(75).height(75).opacity(0)
} else {
Button(label)
.width(label === '0' ? 160 : 75)
.height(75)
.borderRadius(37.5)
.fontSize(this.getButtonFontSize(label))
.fontWeight(FontWeight.Bold)
.backgroundColor(this.getButtonColor(label))
.fontColor(this.getButtonTextColor(label))
.margin(5)
.onClick(() => { this.onButtonClick(label) })
}
})
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding({ left: 10, right: 10 })
})
}
.layoutWeight(1)
.width('100%')
.backgroundColor('#000000')
.padding({ bottom: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
}
这里用到了 ArkUI 的 ForEach 来做列表渲染——外层 ForEach 遍历行,内层 ForEach 遍历每行的按钮。按钮的属性(宽度、颜色、字号)由辅助方法根据按钮文本动态计算,避免了大量的条件判断代码。
六、运行与调试
6.1 替换代码
打开 entry/src/main/ets/pages/Index.ets,将文件内容全选替换为本文提供的完整代码。DevEco Studio 会自动保存并触发增量编译,通常几秒钟就能完成。
6.2 启动模拟器
如果还没有创建模拟器,点击 DevEco Studio 右侧的 Device Manager,选择手机类型,下载推荐的系统镜像,创建并启动模拟器。首次启动模拟器可能需要 2-5 分钟等待开机。
6.3 运行应用
在 IDE 顶部的设备列表中选择已启动的模拟器,点击绿色 Run(▶) 按钮。DevEco Studio 会自动编译、打包、安装并在模拟器中启动应用。
七、运行效果
📸
功能测试用例
替换代码完成后,用以下几个测试用例验证计算器功能是否正常:
| 测试操作 | 预期结果 | 说明 |
|---|---|---|
按 12 + 34 = |
显示 46 |
基础加法 |
按 100 - 23 = |
显示 77 |
基础减法 |
按 7 × 8 = |
显示 56 |
基础乘法 |
按 100 ÷ 4 = |
显示 25 |
基础除法 |
按 5 ÷ 0 = |
显示 Error |
除零保护 |
按 C |
显示 0 |
清空功能 |
按 100 ± |
显示 -100 |
正负切换 |
按 200 % |
显示 2 |
百分比计算 |
按 5 + 3 + 2 = |
显示 10 |
链式运算 |
按 0.1 + 0.2 = |
显示 0.3 |
浮点数精度处理 |
按 9 连续按 20 次 |
最多显示 15 位 | 长度限制 |
八、扩展思路
一个基础版计算器做完后,如果你还想继续挑战,可以考虑以下扩展方向:
8.1 科学计算器
在现有布局基础上增加第二页功能按钮,包括三角函数(sin、cos、tan)、对数(log、ln)、平方根、幂运算等。横屏时自动切换到科学模式。
8.2 历史记录
使用 @StorageLink 或轻量级数据库,保存每次计算的历史记录,支持上滑查看和回点复用。
8.3 键盘支持
通过监听 onKeyEvent 事件,支持外接键盘或蓝牙键盘的数字和运算符输入,甚至可以用电脑键盘操作模拟器中的应用。
8.4 主题切换
增加浅色主题和彩色主题设置,使用 @State 控制颜色变量的切换,实现一键换肤。
九、总结
本文通过一个完整的计算器案例,带你走过了 HarmonyOS 原生应用开发 从创建项目到运行调试的全流程:
- ✅ 使用 DevEco Studio 向导创建项目,省去手动配置的麻烦
- ✅ 理解 ArkUI 的项目结构,知道哪些文件需要修改
- ✅ 学习 ArkTS 的状态管理,掌握
@State的使用时机 - ✅ 实现完整的按钮布局和动态样式
- ✅ 编写严谨的运算逻辑,涵盖除零、溢出、链式运算等边缘情况
- ✅ 在模拟器中运行并测试
计算器虽小,却涵盖了 ArkUI 开发最核心的知识点:声明式 UI、状态管理、事件处理、列表渲染、条件样式。掌握了这些,你就具备了开发更复杂鸿蒙应用的基础能力。
希望这篇文章对正在学习鸿蒙开发的你有所帮助。欢迎在评论区交流你的学习心得或遇到的问题,也期待看到你在此基础上创作出更有趣的应用!
项目完整代码可私信获取。如果你觉得这篇文章有用,请点赞收藏,这是对我最大的鼓励!
更多推荐


所有评论(0)