一、项目开发概述

随着鸿蒙操作系统生态的持续完善,基于 ArkTS 与 ArkUI 的原生应用开发,已经成为移动端入门开发的主流学习方向。相较于传统移动端开发技术栈,鸿蒙声明式开发范式语法更简洁、逻辑更清晰、数据与视图的关联性更强,非常适合编程零基础、移动端开发新手入门学习。

在所有鸿蒙入门实战项目中,简易计算器是最具代表性的综合性练习项目。该项目整合了页面布局搭建、静态样式美化、动态数据绑定、用户点击交互、逻辑条件判断、数值运算处理、程序异常容错等一系列基础开发技能,能够全方位锻炼开发者的基础编码能力与程序逻辑思维。

本次开发全程基于 HarmonyOS NEXT 版本,使用官方原生 ArkTS 语言与 ArkUI 声明式组件开发,不引入任何第三方开源框架、无复杂工程配置、无高阶封装语法,所有代码均为原生基础写法,兼容性强、可读性高、极易上手。

本文将从环境搭建、工程创建、变量定义、页面布局、样式优化、交互逻辑编写、运算核心实现、功能容错处理、项目测试优化等完整流程,一步步完成简易计算器的开发。全文内容详实、步骤完整、注释清晰、细节拉满,适合零基础新手系统学习,也可作为课程设计、实训作业、技术博客原创内容使用。

1.1 项目实现功能介绍

本次开发的简易计算器对标系统原生计算器基础功能,实现日常使用的完整基础运算能力,具体功能如下:

  1. 数字输入功能:支持 0-9 多位数字连续输入,支持整数、小数混合输入;
  2. 小数点规范输入:添加输入拦截逻辑,禁止同一数值内重复输入小数点,规避非法数值格式;
  3. 基础四则运算:完整实现加法、减法、乘法、除法四种日常数学运算;
  4. 一键重置功能:配备 C 清空按键,一键重置所有运算数据与页面显示,恢复初始状态;
  5. 异常容错处理:针对数学运算中最常见的除数为零问题做专属拦截,避免程序报错、闪退、卡死;
  6. 连续运算能力:运算完成后,结果可直接作为新的运算基数,支持多轮连续复合运算;
  7. 数值格式优化:自动精简浮点运算多余小数位,解决原生浮点运算精度冗余问题,展示结果更规整。

1.2 技术栈与开发环境

为保证项目通用性与稳定性,本次开发全部采用官方稳定版本工具与原生技术组件,具体环境配置如下:

  • 开发工具:DevEco Studio 最新稳定版
  • 目标系统:HarmonyOS NEXT
  • 开发语言:ArkTS(鸿蒙原生开发语言)
  • UI 开发框架:ArkUI 声明式开发范式
  • 运行设备:鸿蒙模拟器 / 鸿蒙真机设备

1.3 项目开发核心知识点

通过完成本项目开发,能够扎实掌握鸿蒙入门阶段核心知识点,所有知识点均为日常开发高频刚需内容:

  1. 鸿蒙项目工程的创建、文件结构认知、基础运行流程;
  2. @State 响应式状态变量的定义与使用,理解数据驱动视图的核心机制;
  3. 主流基础布局组件:Column 纵向布局、Grid 网格布局的实战使用;
  4. 基础交互组件:Text 文本展示组件、Button 按钮组件的属性配置与样式美化;
  5. 组件点击事件绑定与事件分发逻辑;
  6. 字符串处理、数值类型转换、条件分支判断、基础语法综合运用;
  7. 移动端应用基础容错设计与用户体验优化思路。

二、开发环境搭建与工程创建

2.1 开发工具介绍

DevEco Studio 是华为官方专为鸿蒙应用开发打造的集成开发工具,集成了项目创建、代码编写、语法校验、实时预览、编译运行、调试报错等全套开发能力,完全适配 HarmonyOS NEXT 系统应用开发。该工具对鸿蒙原生语法兼容性好、报错提示精准、编译速度快、内置模拟器适配完善,零基础开发者完成安装配置后,就可以直接启动项目开发工作,无需额外配置复杂环境变量与依赖包。

对于初次接触该工具的新手而言,建议使用官方推荐的稳定版本,避免测试版出现未知兼容问题。工具安装流程遵循官方引导即可,安装完成后默认会附带鸿蒙模拟器镜像,可直接用于应用预览与测试,当然也可以使用实体鸿蒙手机连接电脑进行真机调试,两种运行方式本文都会在后续测试环节进行说明。

2.2 全新项目创建流程

正式编码前,我们首先需要创建一个全新的鸿蒙空白工程,详细操作步骤如下:

  1. 双击打开 DevEco Studio,在软件启动后的欢迎界面中,点击 Create Project 选项,进入项目创建向导;
  2. 在模板选择页面,众多工程模板里选择 Empty Ability 空白模板,该模板不会自带冗余业务代码,是从零自定义开发应用的首选模板,选中后点击右下角「Next」继续;
  3. 进入工程配置页面,依次填写相关信息:项目名称建议使用纯英文,命名简洁易懂;设备类型勾选 Phone 手机类型,匹配计算器移动端使用场景;开发语言选定 ArkTS
  4. 选择本地文件夹作为项目存储路径,路径建议设置为纯英文路径,不要包含中文、空格与特殊字符,防止编译报错;
  5. 确认所有配置无误后,点击「Finish」完成创建,此时工具会自动拉取项目依赖、初始化目录文件,等待加载完毕即可。

2.3 项目核心文件说明

工程加载完成后,我们可以看到标准的鸿蒙项目目录结构。本项目属于单页面简易应用,所有代码都将编写在 entry/src/main/ets/pages/Index.ets 文件中

这个文件是项目默认的首页页面文件,也是应用启动后第一个展示的页面。其余目录文件为工程配置、资源文件、编译脚本等,本次开发无需修改。新手在初期学习阶段,不用深究复杂目录分工,聚焦核心页面文件即可。

三、项目核心逻辑设计与全局变量定义

在动手编写界面和交互代码之前,梳理项目整体运行逻辑、定义全局变量是必不可少的一步。变量是整个应用的数据核心,页面展示内容、运算缓存数据、状态标记全部依靠变量承载,合理的变量定义能够让后续逻辑编写更加顺畅。

3.1 整体交互逻辑梳理

结合日常使用计算器的操作习惯,我们梳理出完整的业务执行流程,方便大家理解每一段代码的设计初衷:

  1. 应用启动初始化,页面显示屏默认展示数字 0,所有运算缓存数据清空,程序处于待输入状态;
  2. 用户点击 0-9 数字键或小数点按键,页面实时刷新展示输入内容,支持多位数连续录入;
  3. 用户点击 +-*/ 任意运算符,程序会将当前显示屏上的数字缓存为第一个运算数,同时记录选中的运算符,并切换为等待输入第二个数字的状态;
  4. 用户继续输入第二个运算数字,输入完成后点击 = 等于键,程序读取两组数字与运算符,执行对应的数学运算;
  5. 运算结束后,页面展示最终计算结果,同时将结果缓存,支持直接基于该结果开展新一轮连续运算;
  6. 若在运算过程中触发「除数为 0」这类非法运算,程序主动拦截异常,展示错误提示,保证应用不会闪退;
  7. 点击 C 清空键,所有变量全部重置,页面回到初始状态,可重新开始新一轮计算。

3.2 全局变量编写与逐行解析

根据上面梳理的逻辑,我们一共需要定义四个全局变量,分别负责页面展示、运算数字缓存、运算符记录、输入状态标记。打开 Index.ets 文件,在组件内部首先编写变量代码:

// 屏幕显示内容,响应式变量,数据变更自动刷新UI

@State showText: string = "0"

// 缓存第一个参与运算的数字

private firstNum: number = 0

// 记录用户选择的运算符

private operator: string = ""

// 标记是否开启新一轮输入

private isNewInput: boolean = true

接下来对每一个变量进行详细讲解,帮助新手理解设计思路与使用场景:

  1. @State showText: string = "0"

@State 是 ArkUI 中最基础也最重要的装饰器,被它修饰的变量称之为响应式变量。鸿蒙声明式开发采用「数据驱动视图」的设计思想,当 showText 的值发生改变时,页面中绑定该变量的 Text 组件会自动刷新内容,不需要开发者手动编写页面更新代码。

该变量类型设置为字符串,初始值为 "0",保证应用启动后显示屏有默认内容,符合大众使用计算器的使用习惯。如果去掉默认值,页面初始状态会为空,影响使用体验。

  1. private firstNum: number = 0

这是一个私有数值类型变量,作用是缓存第一个参与四则运算的数字。当用户点击运算符之后,当前显示屏的数字会转换为数值类型赋值给该变量,全程在后台静默存储数据,不会直接展示在页面上。初始值设置为 0,保证变量初始化合法。

  1. private operator: string = ""

字符串类型私有变量,专门用来记录用户选中的运算符,可选值为 +-*/。程序后续执行运算时,会读取该变量的值,判断需要执行加法、减法、乘法还是除法。初始值设置为空字符串,代表当前未选择任何运算符。

  1. private isNewInput: boolean = true

布尔类型状态标记变量,是优化输入逻辑的关键。true 代表需要清空原有内容、开启全新输入;false 代表在原有内容基础上继续拼接输入。借助这个变量,可以有效解决运算完成后数字重叠、初始多零等问题,让输入逻辑更加贴合原生计算器。

四、UI 界面布局与样式编写

变量定义完成后,我们开始搭建可视化界面。本项目界面分为两大模块:上方的数字显示屏、下方的功能按键区。整体使用 Column 纵向布局实现上下分区,按键区域使用 Grid 网格布局实现规整排列,同时搭配样式属性美化界面,提升视觉效果。

4.1 整体布局思路

页面整体结构从上至下依次为:显示屏 → 功能按键区。

  • 外层容器使用 Column 布局:这是鸿蒙最常用的纵向布局组件,能够让内部组件按照从上到下的顺序依次排列;
  • 显示屏使用独立的 Text 组件实现,单独设置宽高、背景、圆角、文字对齐等样式,模拟真实计算器屏幕;
  • 按键区域使用 Grid + GridItem 组合:Grid 网格布局可以将区域划分为固定行列,均匀分布按钮,非常适合制作键盘、计算器按键这类矩阵式布局;GridItem 作为网格中的每一个单元格,内部放置 Button 按钮组件。

4.2 完整布局代码编写

在组件内 build() 函数中编写布局与样式代码,完整内容如下:

  build() {

    Column() {

      // 顶部显示屏

      Text(this.showText)

        .width("90%")

        .height(120)

        .fontSize(48)

        .textAlign(TextAlign.End)

        .padding({ right: 15 })

        .backgroundColor(0xF5F5F5)

        .borderRadius(12)

        .margin({ top: 30, bottom: 20 })

      // 底部按钮网格区域

      Grid() {

        GridItem() { Button("C").fontSize(28).backgroundColor(0xEEEEEE) }

        GridItem() { Button("/").fontSize(28).backgroundColor(0xFFB74D) }

        GridItem() { Button("*").fontSize(28).backgroundColor(0xFFB74D) }

        GridItem() { Button("-").fontSize(28).backgroundColor(0xFFB74D) }

        GridItem() { Button("7").fontSize(28) }

        GridItem() { Button("8").fontSize(28) }

        GridItem() { Button("9").fontSize(28) }

        GridItem() { Button("+").fontSize(28).backgroundColor(0xFFB74D) }

        GridItem() { Button("4").fontSize(28) }

        GridItem() { Button("5").fontSize(28) }

        GridItem() { Button("6").fontSize(28) }

        GridItem() { Button("=").fontSize(28).backgroundColor(0x42A5F5).fontColor(Color.White) }

        GridItem() { Button("1").fontSize(28) }

        GridItem() { Button("2").fontSize(28) }

        GridItem() { Button("3").fontSize(28) }

        GridItem() { Button(".").fontSize(28) }

        GridItem() { Button("0").fontSize(28).width("100%") }

      }

      .columnsTemplate("1fr 1fr 1fr 1fr")

      .rowsGap(12)

      .columnsGap(12)

      .width("90%")

      .height(420)

    }

    .width("100%")

    .height("100%")

    .justifyContent(FlexAlign.Start)

  }

4.3 布局与样式属性详解

  1. 外层 Column 容器

width("100%") 和 height("100%") 表示容器占满整个手机屏幕宽高;justifyContent(FlexAlign.Start) 表示内部组件靠顶部排列,避免内容居中导致布局错乱。

  1. Text 显示屏组件
  • width("90%"):宽度占屏幕90%,左右预留边距,视觉更美观;
  • height(120):固定高度,模拟屏幕尺寸;
  • fontSize(48):设置文字大小,保证数字清晰可见;
  • textAlign(TextAlign.End):文字右对齐,和主流计算器显示规则保持一致;
  • backgroundColor(0xF5F5F5):设置浅灰色背景,区分屏幕与按键区域;
  • borderRadius(12):设置圆角,弱化直角生硬感;
  • margin:设置上下外边距,拉开屏幕与按键的距离。
  1. Grid 网格布局属性
  • columnsTemplate("1fr 1fr 1fr 1fr"):将网格划分为4列,1fr 代表每一列均分剩余宽度;
  • rowsGap / columnsGap:分别设置行间距、列间距,避免按钮紧密贴合;
  • 整体设置宽高,限定按键区域范围。
  1. Button 按钮样式区分

为不同功能按钮设置差异化背景色,提升辨识度:清空键使用浅灰色、四则运算符使用橙黄色、等于键使用蓝色并搭配白色文字,纯数字键使用默认样式,视觉分区清晰。

4.4 静态界面预览

完成布局代码编写后,点击工具右上角运行按钮,选择模拟器启动应用,此时可以看到完整的静态界面,按钮样式、布局、配色均已生效,暂时无法点击交互。

【截图位置3:计算器静态UI界面预览效果】

五、按钮点击交互逻辑开发

静态界面搭建完成后,接下来实现核心的点击交互功能。我们采用统一事件入口的设计方式:所有按钮绑定同一个点击方法,通过判断按钮上的文本内容,分流执行不同的业务逻辑。这种写法代码结构规整,便于后期维护与修改。

5.1 定义统一点击方法

在组件内部、build 函数外侧,新增 onBtnClick 方法,作为所有按钮的统一点击入口,代码如下:

  /**

   * 所有按钮统一点击事件

   * @param value 按钮上的文本内容

   */

  onBtnClick(value: string): void {

    // 清空按钮逻辑

    if (value === "C") {

      this.showText = "0"

      this.firstNum = 0

      this.operator = ""

      this.isNewInput = true

      return

    }

    // 等于按钮,触发计算

    if (value === "=") {

      this.calcResult()

      return

    }

    // 点击四则运算符,保存第一个数字与运算符号

    if (["+", "-", "*", "/"].includes(value)) {

      this.firstNum = parseFloat(this.showText)

      this.operator = value

      this.isNewInput = true

      return

    }

    // 数字与小数点输入逻辑

    if (this.isNewInput) {

      this.showText = value === "." ? "0." : value

      this.isNewInput = false

    } else {

      // 禁止重复输入小数点

      if (value === "." && this.showText.includes(".")) {

        return

      }

      this.showText += value

    }

  }

5.2 代码分段解析

  1. 清空键 C 逻辑

当点击 C 按钮时,将所有变量恢复为初始值:显示屏重置为 "0"、运算数字清零、运算符置空、输入状态标记恢复为 true,实现一键重置效果,执行 return 终止后续代码运行。

  1. 等于键 = 逻辑

点击等于键后,直接调用后续编写的 calcResult() 运算方法,触发四则运算逻辑。

  1. 四则运算符逻辑

判断点击的内容为 +-*/ 任意运算符后,使用 parseFloat() 将显示屏的字符串转为数值,赋值给 firstNum 完成数字缓存;同时记录运算符,并且将 isNewInput 设为 true,准备接收第二个数字输入。

  1. 数字与小数点输入逻辑

结合 isNewInput 标记区分两种场景:

  • 全新输入状态:直接将按钮内容赋值给显示屏,若点击小数点,则默认补 0.,规范小数格式;
  • 接续输入状态:在原有字符串后方拼接内容,同时增加判断,若当前内容已存在小数点,则拦截二次输入,避免出现非法数值。

5.3 为所有按钮绑定点击事件

回到 Grid 布局中的每一个 GridItem,为内部的 Button 组件补充 onClick 点击绑定,格式统一为:

.onClick(() => this.onBtnClick("按钮文本"))

修改后的完整按钮代码示例:

      Grid() {

        GridItem() { Button("C").fontSize(28).backgroundColor(0xEEEEEE) }

        .onClick(() => this.onBtnClick("C"))

        GridItem() { Button("/").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("/"))

        GridItem() { Button("*").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("*"))

        GridItem() { Button("-").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("-"))

        GridItem() { Button("7").fontSize(28) }

        .onClick(() => this.onBtnClick("7"))

        GridItem() { Button("8").fontSize(28) }

        .onClick(() => this.onBtnClick("8"))

        GridItem() { Button("9").fontSize(28) }

        .onClick(() => this.onBtnClick("9"))

        GridItem() { Button("+").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("+"))

        GridItem() { Button("4").fontSize(28) }

        .onClick(() => this.onBtnClick("4"))

        GridItem() { Button("5").fontSize(28) }

        .onClick(() => this.onBtnClick("5"))

        GridItem() { Button("6").fontSize(28) }

        .onClick(() => this.onBtnClick("6"))

        GridItem() { Button("=").fontSize(28).backgroundColor(0x42A5F5).fontColor(Color.White) }

        .onClick(() => this.onBtnClick("="))

        GridItem() { Button("1").fontSize(28) }

        .onClick(() => this.onBtnClick("1"))

        GridItem() { Button("2").fontSize(28) }

        .onClick(() => this.onBtnClick("2"))

        GridItem() { Button("3").fontSize(28) }

        .onClick(() => this.onBtnClick("3"))

        GridItem() { Button(".").fontSize(28) }

        .onClick(() => this.onBtnClick("."))

        GridItem() { Button("0").fontSize(28).width("100%") }

        .onClick(() => this.onBtnClick("0"))

      }

绑定完成后,数字输入、切换运算符、一键重置等基础交互功能已经可以正常运行,接下来实现核心的四则运算逻辑。

六、四则运算核心方法与异常处理

运算逻辑是计算器的核心功能,我们单独封装 calcResult 方法,统一处理数值转换、分支运算、异常拦截、结果回显等逻辑,同时针对浮点精度、除数为零两大常见问题做优化处理。

6.1 运算方法完整代码

在组件内添加如下代码:

  /**

   * 四则运算核心方法

   */

  calcResult(): void {

    // 未选择运算符,不执行计算

    if (this.operator === "") {

      return

    }

    const secondNum: number = parseFloat(this.showText)

    let result: number = 0

    // 根据运算符执行对应运算

    switch (this.operator) {

      case "+":

        result = this.firstNum + secondNum

        break

      case "-":

        result = this.firstNum - secondNum

        break

      case "*":

        result = this.firstNum * secondNum

        break

      case "/":

        // 处理除数为0的异常场景

        if (secondNum === 0) {

          this.showText = "计算错误"

          this.firstNum = 0

          this.operator = ""

          this.isNewInput = true

          return

        }

        result = this.firstNum / secondNum

        break

    }

    // 限制小数位数,优化浮点显示效果

    result = parseFloat(result.toFixed(6))

    // 更新界面,支持连续运算

    this.showText = result.toString()

    this.firstNum = result

    this.operator = ""

    this.isNewInput = true

  }

6.2 代码逐段解析

  1. 前置判断

首先判断 operator 运算符变量是否为空,如果用户没有选择任何运算符就点击等于键,直接终止方法,不执行无效运算。

  1. 数值转换

使用 parseFloat() 将显示屏的字符串内容转为数值类型,赋值给 secondNum,作为第二个运算数。

  1. switch 分支运算

通过 switch 语句匹配运算符,分别执行加法、减法、乘法、除法运算,将计算结果存入 result 变量。

  1. 除数为零异常拦截

数学规则中,除数不能为 0,因此在除法分支中单独增加判断:如果第二个数字为 0,显示屏展示「计算错误」,同时重置所有变量,终止代码运行,彻底避免应用闪退、卡死问题,这也是程序容错设计的核心部分。

  1. 浮点精度优化

ArkTS 基于 JavaScript 内核,原生浮点运算会出现多余长尾小数。使用 toFixed(6) 保留 6 位小数,再转回数值类型,精简无效小数位,让展示结果更加整洁。

  1. 结果回显与连续运算支持

将最终结果转为字符串赋值给 showText 刷新页面;同时把结果赋值给 firstNum,清空运算符,标记为全新输入状态,此时用户可以直接点击运算符继续计算,实现连续运算功能。

七、项目完整整合代码

至此,所有功能代码全部编写完成。以下是 Index.ets 文件完整可运行源码,大家可以直接复制替换文件中所有内容,保证代码完整性:

@Entry

@Component

struct Index {

  // 屏幕显示内容,响应式变量,数据变更自动刷新UI

  @State showText: string = "0"

  // 缓存第一个参与运算的数字

  private firstNum: number = 0

  // 记录用户选择的运算符

  private operator: string = ""

  // 标记是否开启新一轮输入

  private isNewInput: boolean = true

  /**

   * 所有按钮统一点击事件

   * @param value 按钮上的文本内容

   */

  onBtnClick(value: string): void {

    // 清空按钮逻辑

    if (value === "C") {

      this.showText = "0"

      this.firstNum = 0

      this.operator = ""

      this.isNewInput = true

      return

    }

    // 等于按钮,触发计算

    if (value === "=") {

      this.calcResult()

      return

    }

    // 点击四则运算符,保存第一个数字与运算符号

    if (["+", "-", "*", "/"].includes(value)) {

      this.firstNum = parseFloat(this.showText)

      this.operator = value

      this.isNewInput = true

      return

    }

    // 数字与小数点输入逻辑

    if (this.isNewInput) {

      this.showText = value === "." ? "0." : value

      this.isNewInput = false

    } else {

      // 禁止重复输入小数点

      if (value === "." && this.showText.includes(".")) {

        return

      }

      this.showText += value

    }

  }

  /**

   * 四则运算核心方法

   */

  calcResult(): void {

    // 未选择运算符,不执行计算

    if (this.operator === "") {

      return

    }

    const secondNum: number = parseFloat(this.showText)

    let result: number = 0

    // 根据运算符执行对应运算

    switch (this.operator) {

      case "+":

        result = this.firstNum + secondNum

        break

      case "-":

        result = this.firstNum - secondNum

        break

      case "*":

        result = this.firstNum * secondNum

        break

      case "/":

        // 处理除数为0的异常场景

        if (secondNum === 0) {

          this.showText = "计算错误"

          this.firstNum = 0

          this.operator = ""

          this.isNewInput = true

          return

        }

        result = this.firstNum / secondNum

        break

    }

    // 限制小数位数,优化浮点显示效果

    result = parseFloat(result.toFixed(6))

    // 更新界面,支持连续运算

    this.showText = result.toString()

    this.firstNum = result

    this.operator = ""

    this.isNewInput = true

  }

  build() {

    Column() {

      // 顶部显示屏

      Text(this.showText)

        .width("90%")

        .height(120)

        .fontSize(48)

        .textAlign(TextAlign.End)

        .padding({ right: 15 })

        .backgroundColor(0xF5F5F5)

        .borderRadius(12)

        .margin({ top: 30, bottom: 20 })

      // 底部按钮网格区域

      Grid() {

        GridItem() { Button("C").fontSize(28).backgroundColor(0xEEEEEE) }

        .onClick(() => this.onBtnClick("C"))

        GridItem() { Button("/").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("/"))

        GridItem() { Button("*").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("*"))

        GridItem() { Button("-").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("-"))

        GridItem() { Button("7").fontSize(28) }

        .onClick(() => this.onBtnClick("7"))

        GridItem() { Button("8").fontSize(28) }

        .onClick(() => this.onBtnClick("8"))

        GridItem() { Button("9").fontSize(28) }

        .onClick(() => this.onBtnClick("9"))

        GridItem() { Button("+").fontSize(28).backgroundColor(0xFFB74D) }

        .onClick(() => this.onBtnClick("+"))

        GridItem() { Button("4").fontSize(28) }

        .onClick(() => this.onBtnClick("4"))

        GridItem() { Button("5").fontSize(28) }

        .onClick(() => this.onBtnClick("5"))

        GridItem() { Button("6").fontSize(28) }

        .onClick(() => this.onBtnClick("6"))

        GridItem() { Button("=").fontSize(28).backgroundColor(0x42A5F5).fontColor(Color.White) }

        .onClick(() => this.onBtnClick("="))

        GridItem() { Button("1").fontSize(28) }

        .onClick(() => this.onBtnClick("1"))

        GridItem() { Button("2").fontSize(28) }

        .onClick(() => this.onBtnClick("2"))

        GridItem() { Button("3").fontSize(28) }

        .onClick(() => this.onBtnClick("3"))

        GridItem() { Button(".").fontSize(28) }

        .onClick(() => this.onBtnClick("."))

        GridItem() { Button("0").fontSize(28).width("100%") }

        .onClick(() => this.onBtnClick("0"))

      }

      .columnsTemplate("1fr 1fr 1fr 1fr")

      .rowsGap(12)

      .columnsGap(12)

      .width("90%")

      .height(420)

    }

    .width("100%")

    .height("100%")

    .justifyContent(FlexAlign.Start)

  }

}

八、功能全量测试与效果验证

代码整合完成后,启动应用,逐项测试所有功能,验证每一个模块是否正常运行,同时验证容错逻辑是否生效。测试分为基础输入测试、常规运算测试、异常场景测试、辅助功能测试四大板块。

8.1 数字与小数点输入测试

操作流程:依次点击 0-9 数字按键,测试多位数连续输入;多次点击小数点按键,测试拦截逻辑。

预期效果:可正常输入多位整数;重复点击小数点时,页面不会出现多个小数点,数值格式合法。

8.2 常规四则运算测试

分别测试加法、减法、乘法、除法基础运算,示例操作:

  1. 输入 25 → 点击 + → 输入 15 → 点击 =,查看结果;
  2. 输入 99 → 点击 - → 输入 33 → 点击 =,查看结果;
  3. 输入 8 → 点击 * → 输入 7 → 点击 =,查看结果;
  4. 输入 80 → 点击 / → 输入 4 → 点击 =,查看结果。

预期效果:所有运算均可精准输出结果,浮点数值自动精简多余小数位。

8.3 除数为零异常测试

操作流程:任意输入一个数字,点击 / 除号,再输入 0,最后点击 = 等于键。

预期效果:页面展示「计算错误」,应用不会闪退、卡死,点击 C 可正常重置。

8.4 清空与连续运算测试

  1. 清空功能:任意输入数字、完成运算后,点击 C 键,页面立刻重置为初始 0,所有缓存数据清空;
  2. 连续运算:完成一次运算得出结果后,直接点击运算符、新数字、等于键,可基于上一次结果继续运算。

两项功能均正常运行,代表整体逻辑闭环完整。

九、开发常见问题分析与解决方案

在编写和运行代码的过程中,新手大概率会遇到各类报错、功能异常问题,本章节汇总高频问题、报错原因以及对应的解决办法,方便大家自主排查问题。

9.1 页面内容无法刷新

现象:点击数字按键,显示屏内容始终保持初始 0,没有任何变化。

原因:承载页面内容的变量 showText 没有添加 @State 装饰器,失去响应式能力,数据改变无法驱动视图刷新。

解决方案:检查变量定义代码,为展示变量补充 @State 装饰器。

9.2 可以重复输入多个小数点

现象:连续点击小数点,页面出现 1.2.3 这类非法格式。

原因:缺少小数点拦截判断逻辑,未使用 includes() 方法检测字符串内是否已存在小数点。

解决方案:在小数点输入分支中增加判断,已存在小数点则直接终止逻辑。

9.3 除法运算时应用闪退

现象:输入数字除以 0 后,应用直接退出、崩溃。

原因:未做除数为零的异常拦截,数学非法运算导致程序运行异常。

解决方案:在除法分支中添加 secondNum === 0 判断,主动拦截非法运算并重置状态。

9.4 运算结果出现超长浮点小数

现象:小数运算后,结果出现大量长尾无效小数,例如 0.1 + 0.2 = 0.30000000000000004

原因:ArkTS 浮点运算原生精度问题,是语言底层特性。

解决方案:使用 toFixed(n) 方法限定小数保留位数,精简展示内容。

9.5 点击等于键无任何反应

现象:输入两个数字后点击等于键,页面没有计算结果。

原因:未选择运算符,operator 变量始终为空,前置判断直接终止运算方法。

解决方案:按照标准流程操作,先点击运算符再输入第二个数字。

十、项目总结与知识点拓展

回顾整个开发流程,我们从工程创建、变量定义、界面布局、交互逻辑、运算实现、异常处理完整走完了一套鸿蒙小型应用的开发流程,巩固了多项入门核心技能:

  1. 熟练掌握 DevEco Studio 基础操作、鸿蒙项目创建与运行流程,理解单页面应用的开发模式;
  2. 深度理解 @State 响应式变量与数据驱动视图的核心思想,这是鸿蒙声明式开发的核心基础;
  3. 掌握 Column、Grid、GridItem、Text、Button 五大高频基础组件的属性配置与实战用法,理解不同布局组件的适用场景;
  4. 学会按钮点击事件绑定、事件分发逻辑,掌握 ifswitch 分支语句在业务逻辑中的综合运用;
  5. 掌握字符串与数值之间的相互转换方法,了解移动端应用基础容错设计的思路与实现方式;
  6. 建立基础的程序分层思维:数据层(全局变量)、视图层(UI布局)、逻辑层(交互与运算方法)。

Logo

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

更多推荐