HarmonyOS ArkTS 实战:实现一个标准计算器
HarmonyOS ArkTS 实战:实现一个标准计算器
项目效果
本文实现的是一个基于 HarmonyOS 和 ArkTS 的标准计算器。项目中使用 ArkUI 组件完成页面布局,通过@State管理状态数据,实现加减乘除四则运算、小数运算、清除、退格、正负切换和百分比等标准计算器功能。
最终运行效果如下:
页面主要包含以下内容:
- 顶部应用标题;
- 上方显示区域,显示计算表达式;
- 大字体显示当前数字;
- 下方4×5按钮网格;
- 数字键0-9;
- 加减乘除运算符;
- 等于、清除、退格、小数点、正负、百分比按钮;
- 页面整体采用 ArkUI 声明式布局。
本文重点是演示如何在 HarmonyOS 项目中使用 ArkTS 和 ArkUI 实现一个计算器单页面应用。项目代码主要写在entry/src/main/ets/pages/Index.ets文件中,适合作为 HarmonyOS ArkTS 入门到进阶之间的练习案例。
前言
计算器是每个手机必备的工具,也是学习编程最经典的入门项目。实现一个计算器看起来简单,但要处理好各种边界情况并不容易:连续运算、小数、除以零、运算符切换等等。
从应用开发角度来看,这个项目不依赖后端接口,也不需要数据库,但能练习 ArkTS 中的状态管理、网格布局、按钮交互、表达式计算和边界情况处理等内容。
本文基于 HarmonyOS 和 ArkTS 实现一个标准计算器。支持加减乘除四则运算,有标准计算器的所有常用功能,界面简洁,操作流畅。
这个项目的核心不是简单的加减乘除,而是正确处理各种计算状态和边界情况。每一次按钮点击,都会改变计算器的状态,这正是 ArkUI 声明式开发的基本思想。
一、项目目标
本次实践主要实现以下目标:
- 创建 HarmonyOS ArkTS 页面;
- 使用
@Entry和@Component定义页面组件; - 使用
@State管理页面状态; - 实现0-9数字输入;
- 加减乘除四则运算;
- 小数点输入;
- 等于计算结果;
- 全部清除(AC);
- 退格删除;
- 正负号切换;
- 百分比计算;
- 连续运算支持;
- 使用
@Builder封装计算器按钮; - 完成一个可以运行的标准计算器页面。
这个项目虽然是单页面应用,但它实现了完整的计算器功能,处理了各种边界情况,比普通静态页面更适合练习 ArkTS。
二、技术栈
| 类型 | 内容 |
|---|---|
| 开发方向 | HarmonyOS 应用开发 |
| 开发语言 | ArkTS |
| UI 框架 | ArkUI |
| SDK 版本 | HarmonyOS API 23 及以上 |
| 工程模型 | Stage 模型 |
| 核心组件 | Text / Button / Column / Row / Grid |
| 状态管理 | @State |
| 数据处理 | 四则运算 / 状态机 / 边界处理 |
| 项目入口 | entry/src/main/ets/pages/Index.ets |
| 运行平台 | 模拟器或真机 |
本项目是 HarmonyOS 原生 ArkTS 项目。页面主体由 ArkUI 组件构建,核心逻辑写在Index.ets文件中,不依赖后端接口,也不需要额外配置数据库。
三、为什么选择标准计算器项目
标准计算器适合作为 ArkTS 练习项目,主要有以下几个原因。
第一,经典入门项目。几乎所有编程入门都会做计算器,是检验基础能力的好项目。
第二,实用性强。计算器是日常最常用的工具之一。
第三,适合练习状态管理。计算器有多种状态:输入数字、输入运算符、显示结果等。
第四,适合练习网格布局。计算器按钮是规则的网格,练习Grid或Flex布局。
第五,适合练习边界处理。除以零、多个小数点、连续运算符等情况都需要处理。
第六,扩展空间比较大。基础功能完成后,可以继续增加科学计算、历史记录、单位换算、进制转换和主题切换。
在本项目中,当前数字、运算符、上一个数字是核心状态。所有计算逻辑都基于这些状态。
四、功能规则说明
按钮布局:
| AC | +/- | % | ÷ |
|---|---|---|---|
| 7 | 8 | 9 | × |
| 4 | 5 | 6 | - |
| 1 | 2 | 3 | + |
| 0 | . | ⌫ | = |
运算规则:
- 输入数字时连续输入组成多位数
- 点击运算符后保存当前数字,准备输入下一个
- 再次点击运算符时先计算之前的结果
- 点击等于显示最终结果
- AC清除所有状态
- 退格删除最后一位
- +/-切换正负号
- %将当前数字除以100
- 除以0显示Error
五、项目结构
本项目主要修改首页文件:
entry └── src └── main └── ets └── pages └── Index.ets其中:
| 文件 | 作用 |
|---|---|
| Index.ets | 编写页面结构、状态数据和计算逻辑 |
本文不涉及复杂路由,也不需要额外创建多个页面。对于练习项目来说,把主要逻辑集中在一个Index.ets文件中更方便理解。
六、核心实现思路
本项目的核心流程如下:
- 使用
@State保存当前显示数字、上一个数字、当前运算符; - 点击数字按钮追加数字;
- 点击运算符保存当前数字和运算符;
- 点击等于时根据运算符计算结果;
- 处理小数点,确保只有一个;
- 处理正负切换;
- 处理百分比;
- 处理退格;
- AC重置所有状态;
- 处理除以零等错误情况。
项目中最重要的状态变量如下:
@State display: string = '0' @State previousValue: number = 0 @State operator: string = '' @State shouldResetDisplay: boolean = false @State expression: string = ''其中:
| 状态变量 | 作用 |
|---|---|
| display | 当前显示的数字 |
| previousValue | 上一个操作数 |
| operator | 当前运算符 |
| shouldResetDisplay | 输入下一个数字时是否重置显示 |
| expression | 表达式显示 |
这些状态构成了计算器的状态机,正确处理状态转换是计算器正确运行的关键。
七、Index.ets 完整代码
打开文件:
entry/src/main/ets/pages/Index.ets将其中内容替换为下面代码:
@Entry@Componentstruct Index{@Statedisplay:string='0'@StatepreviousValue:number=0@Stateoperator:string=''@StateshouldResetDisplay:boolean=false@Stateexpression:string=''privateinputNumber(num:string):void{if(this.shouldResetDisplay){this.display=numthis.shouldResetDisplay=false}else{if(this.display==='0'&&num!=='.'){this.display=num}elseif(num==='.'&&this.display.includes('.')){return}else{this.display+=num}}}privateinputOperator(op:string):void{letcurrentValue=parseFloat(this.display)if(this.operator!==''&&!this.shouldResetDisplay){this.calculate()currentValue=parseFloat(this.display)}this.previousValue=currentValuethis.operator=opthis.shouldResetDisplay=truethis.expression=`${this.formatNumber(this.previousValue)}${this.getOperatorSymbol(op)}`}privatecalculate():void{if(this.operator==='')returnletcurrentValue=parseFloat(this.display)letresult=0switch(this.operator){case'+':result=this.previousValue+currentValuebreakcase'-':result=this.previousValue-currentValuebreakcase'*':result=this.previousValue*currentValuebreakcase'/':if(currentValue===0){this.display='Error'this.operator=''this.shouldResetDisplay=truethis.expression=''return}result=this.previousValue/currentValuebreak}this.expression=`${this.formatNumber(this.previousValue)}${this.getOperatorSymbol(this.operator)}${this.formatNumber(currentValue)}=`this.display=this.formatResult(result)this.operator=''this.shouldResetDisplay=true}privateclearAll():void{this.display='0'this.previousValue=0this.operator=''this.shouldResetDisplay=falsethis.expression=''}privatebackspace():void{if(this.shouldResetDisplay||this.display==='Error')returnif(this.display.length===1||(this.display.length===2&&this.display.startsWith('-'))){this.display='0'}else{this.display=this.display.substring(0,this.display.length-1)}}privatetoggleSign():void{if(this.display==='0'||this.display==='Error')returnif(this.display.startsWith('-')){this.display=this.display.substring(1)}else{this.display='-'+this.display}}privatepercent():void{letvalue=parseFloat(this.display)this.display=this.formatResult(value/100)this.shouldResetDisplay=true}privateformatNumber(num:number):string{if(Number.isInteger(num)){returnnum.toString()}returnnum.toString()}privateformatResult(num:number):string{if(Number.isInteger(num)){returnnum.toString()}returnparseFloat(num.toFixed(10)).toString()}privategetOperatorSymbol(op:string):string{switch(op){case'+':return'+'case'-':return'-'case'*':return'×'case'/':return'÷'default:returnop}}@BuilderCalcButton(text:string,color:string,bgColor:string,onClick:()=>void,flex:number=1){Button(text).layoutWeight(flex).height(64).fontSize(24).fontWeight(FontWeight.Medium).fontColor(color).backgroundColor(bgColor).borderRadius(32).onClick(onClick)}build(){Column(){Text('计算器').fontSize(28).fontWeight(FontWeight.Bold).fontColor('#182431').margin({top:22})Text('基于 HarmonyOS ArkTS 的标准计算器').fontSize(14).fontColor('#6B7280').margin({top:8,bottom:20})Column(){Blank()if(this.expression){Text(this.expression).fontSize(18).fontColor('#9CA3AF').width('100%').textAlign(TextAlign.End).margin({bottom:8})}Text(this.display).fontSize(56).fontWeight(FontWeight.Light).fontColor('#182431').width('100%').textAlign(TextAlign.End).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})}.width('100%').height(180).padding(20).backgroundColor(Color.White).borderRadius(16).margin({bottom:16}).shadow({radius:8,color:'#08000000',offsetX:0,offsetY:2})Column(){Row(){this.CalcButton('AC','#FFFFFF','#9CA3AF',()=>{this.clearAll()})this.CalcButton('+/-','#FFFFFF','#A5A5A5',()=>{this.toggleSign()})this.CalcButton('%','#FFFFFF','#A5A5A5',()=>{this.percent()})this.CalcButton('÷','#FFFFFF','#FF9F0A',()=>{this.inputOperator('/')})}.width('100%').margin({bottom:12})Row(){this.CalcButton('7','#FFFFFF','#333333',()=>{this.inputNumber('7')})this.CalcButton('8','#FFFFFF','#333333',()=>{this.inputNumber('8')})this.CalcButton('9','#FFFFFF','#333333',()=>{this.inputNumber('9')})this.CalcButton('×','#FFFFFF','#FF9F0A',()=>{this.inputOperator('*')})}.width('100%').margin({bottom:12})Row(){this.CalcButton('4','#FFFFFF','#333333',()=>{this.inputNumber('4')})this.CalcButton('5','#FFFFFF','#333333',()=>{this.inputNumber('5')})this.CalcButton('6','#FFFFFF','#333333',()=>{this.inputNumber('6')})this.CalcButton('-','#FFFFFF','#FF9F0A',()=>{this.inputOperator('-')})}.width('100%').margin({bottom:12})Row(){this.CalcButton('1','#FFFFFF','#333333',()=>{this.inputNumber('1')})this.CalcButton('2','#FFFFFF','#333333',()=>{this.inputNumber('2')})this.CalcButton('3','#FFFFFF','#333333',()=>{this.inputNumber('3')})this.CalcButton('+','#FFFFFF','#FF9F0A',()=>{this.inputOperator('+')})}.width('100%').margin({bottom:12})Row(){this.CalcButton('0','#FFFFFF','#333333',()=>{this.inputNumber('0')},2)this.CalcButton('.','#FFFFFF','#333333',()=>{this.inputNumber('.')})this.CalcButton('⌫','#FFFFFF','#333333',()=>{this.backspace()})this.CalcButton('=','#FFFFFF','#FF9F0A',()=>{this.calculate()})}.width('100%')}.width('100%').padding(16).backgroundColor(Color.White).borderRadius(16).shadow({radius:8,color:'#08000000',offsetX:0,offsetY:2})}.width('100%').height('100%').padding({left:18,right:18}).backgroundColor('#F5F7FA')}}八、代码实现说明
1. 状态机设计
计算器有几个关键状态:
- 初始状态:显示0,等待输入
- 输入数字:追加数字到display
- 输入运算符:保存当前值,准备输入下一个数
- 显示结果:等于后显示结果,下次输入数字时重置
2. 连续运算
点击运算符时如果已有运算符,先计算之前的结果,再保存新的运算符,支持连续运算。
3. 边界处理
- 除以零:显示Error
- 多个小数点:阻止输入第二个小数点
- 开头的0:输入第一个非0数字时替换0
- 退格:删到0就不再删了
- 长数字:自动截断,避免溢出
4. 按钮样式
模仿iOS计算器风格:
- 数字键:深灰背景白字
- 功能键:浅灰背景白字
- 运算符:橙色背景白字
- 圆角按钮,间距均匀
5. 表达式显示
计算时显示完整表达式,让用户清楚看到计算过程。
6. 结果格式化
去掉浮点数计算产生的多余小数位,避免显示类似0.30000000000000004的结果。
九、运行项目
代码编写完成后,在DevEco Studio中运行项目。测试加减乘除运算,测试小数、连续运算、清除、退格、正负、百分比等功能,验证除以零的错误处理。
十、开发中遇到的问题
- 连续运算:点击第二个运算符时要先计算前面的结果
- 浮点数精度:toFixed后转字符串去掉多余的0
- 状态重置:等于后输入新数字要重置显示
- 按钮宽度:0号键占两格宽度,其他按钮等宽
十一、总结
本文基于HarmonyOS和ArkTS实现了一个标准计算器。项目通过状态机管理计算状态,实现了完整的四则运算和各种计算器功能,处理了各种边界情况。这个项目展示了ArkTS中状态管理、网格布局和逻辑处理的基本方法,适合作为入门练习项目。
