鸿蒙ArkTS实战:从零打造智能表达式计算器(附状态管理+路由传参核心实现)

news2025/4/14 16:56:50

还在为组件状态混乱、页面跳转丢参数而头疼?
这篇博客将揭秘如何用鸿蒙ArkTS打造一个漂亮美观的智能计算器:
✅ 输入完整表达式,秒出结果——字符串切割简单计算
✅ 状态管理黑科技——@Provide/@Consume 实现跨组件实时响应
✅ 路由传参实战——历史记录页面跳转不丢数据,跨页面传参
✅ 代码即设计稿——ArkTS声明式UI开发,代码比PPT更直观

快捷跳转到想看的地方

  • 一、项目展示
  • 二、技术栈讲解
  • 三、项目结构说明
  • 四、核心代码实现
    • 1. `Index.ets`父组件总布局查看
    • 2. `navbar.ets`实现
    • 3. `listView.ets` 展示区的实现
    • 4. `buttonView.ets` 按钮区的代码说明
  • 五、点赞收藏支持一下吧,代码源码私信我

一、项目展示

这里展示了计算器的加减乘除取余等基本操作,展示了历史记录功能,展示了帮助弹窗

在这里插入图片描述

二、技术栈讲解

  • 使用arkUI通用组件开发
  • 自定义弹窗的实现
  • 使用@State,@Link, @Provide, @Consume进行状态管理
  • 使用router实现路由跳转以及参数携带
  • 使用分模块化结构高效开发

三、项目结构说明

src/  //存放所有与项目逻辑相关的代码文件
├── main/ 	//包含应用程序的核心模块和组件。
│   ├── ets/	//用于存放与 ArkTS(或类似框架)相关的代码。
│   │   ├── common/	//存放可复用的工具类、辅助函数等。
│   │   ├── data/ 	//存放数据,这里的data层是不必要的,由于组件数据较多。写在文件里有点乱,这里分了一层
│   │   ├── entryability/ 	//存放与应用入口相关的逻辑,例如启动时的初始化逻辑。
│   │   ├── entrybackupability/	//存放备用入口逻辑,可能用于异常情况下的备份入口。
│   │   ├── views/ //存放页面中的组件
│   │   ├── pages/ //存放各个页面的逻辑、UI 组件等。
│   │   └── util/ //存放通用工具函数、辅助方法等。
│   └── module.json5 //项目配置文件
└── resources/ //存放静态资源文件,如图片、字体、样式文件等。

四、核心代码实现

1. Index.ets父组件总布局查看

import buttonView from '../views/buttonView'
import navbar from '../views/navbar'
import resultView from '../views/resultView'

@Entry
@Component
struct Index {

  @State calculatorResultText: string = "" //存储打印在结果区的最终结果
  @State res: number = 0 //存储计算器内部逻辑计算的值
  @Provide list: string[] = [] // 存储历史记录数组

  build() {
    Scroll(){
      Column(){
        navbar() // 导航栏
        resultView({calculatorResultText: this.calculatorResultText, res: this.res}) // 结果打印区
        buttonView({calculatorResultText: this.calculatorResultText, res: this.res}) // 按钮控制区
      }
    }
    .scrollBar(BarState.Off)
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    //控制模拟器展示安全区为全屏展示
    .backgroundColor('#1C2220')
    .width('100%')
    .height('100%')
  }
}

2. navbar.ets实现

// navbar.ets
import app from '@system.app';

@Component
export default struct navbar {

//定义两个弹窗控制器,来控制弹窗的行为
  dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample(),
  })

  controller: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample2({}),
  })

  build() {
    Row(){
      // 软件logo和名字
      Row(){
        Image($r('app.media.calculator'))
          .width(20)
          .margin({left: 20,right: 20})
        Text('小东计算器')
          .fontColor('#DEDFDF')
      }
      // 软件的一些系统功能
      Row({space: 10}){
        Text("帮助").fontColor('#DEDFDF')
          .onClick(() => {
          	//触发弹窗
            this.dialogController.open()
           })
        Text("设置").fontColor('#DEDFDF')
          .onClick(() => {
          	//触发弹窗2
            this.controller.open()
          })
        Text("退出").fontColor('#DEDFDF')
          .onClick(() => {
            app.terminate()
          })
      }
      .margin({right: 20})
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

//自定义弹窗
@CustomDialog
struct CustomDialogExample {
  controller: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample({}),
  })

  //小东计算器的帮助文档
  build() {
    Column() {
      // 标题
      Text('小东计算器使用帮助')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 16 })
        .fontColor($r('app.string.calculator_text_color'))

      // 帮助内容
      Text('本计算器支持四则运算(+-×÷),输入数字后点击运算符继续计算,按等号(=)显示结果。\n\n'
        + '特殊功能:\n'
        + '• CE:清除当前输入\n'
        + '• C:完全重置计算器\n'
        + '• DE:删除最后一位数字\n'
        + '• 小数点:点击 . 输入小数部分')
        .fontSize(16)
        .lineHeight(24)
        .fontColor($r('app.string.calculator_text_color'))
    }
    .justifyContent(FlexAlign.Center)
    .padding(20)
    .width('100%')
    .backgroundColor($r('app.string.calculator_bk_color'))
  }
}

@CustomDialog
struct CustomDialogExample2 {
  controller: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample2({}),
  })

  //设置
  build() {
    Column() {
      //日间模式和夜间模式切换按钮
      Row() {
        Text('日间模式').fontColor($r('app.string.calculator_text_color'))
        Toggle({ type: ToggleType.Switch, isOn: true })
      }
    }.justifyContent(FlexAlign.Center)
    .padding(20)
    .width('100%')
    .backgroundColor($r('app.string.calculator_bk_color'))
  }
}

3. listView.ets 展示区的实现

/**
 * 计算器结果视图组件
 * 
 * @Component 声明为可复用的自定义组件
 * 
 * @State isUnFold - 控制模式选择下拉框的展开状态
 * @State calculatorState - 当前计算器模式(标准/科学)
 * @Link calculatorResultText - 双向绑定的计算结果文本
 * @Link res - 双向绑定的计算结果数值
 * @Consume list - 消费上下文中的历史记录列表
 * @State isHover - 按钮悬停状态标识
 * @State m - M系列功能按钮的标签数组
 */
@Component
export default struct resultView {
  // ... 状态变量声明保持不变

  build() {
    Column() {
      /* 顶部操作栏 - 包含模式切换按钮和历史记录入口 */
      Row(){
        // 左侧操作区:模式切换按钮组
        Row(){
          // 模式展开/收起触发器
          Image($r('app.media.ic_grid_setting'))
            .onClick(() => { this.isUnFold = !this.isUnFold })

          // 当前模式显示文本
          Text(this.calculatorState)
            .margin({left: 20, right: 20})

          // 辅助功能按钮
          Image($r('app.media.ic_global_menu'))
        }

        // 右侧操作区:历史记录入口
        Row(){
          Image($r('app.media.ic_time'))
            .onClick(() => { router.pushUrl({ url: 'pages/AfterCheck' }) })
        }
      }
      .justifyContent(FlexAlign.SpaceBetween)

      /* 结果展示区域 - 包含模式选择下拉框和计算结果显示 */
      Stack(){
        // 模式选择下拉框(条件渲染)
        if (this.isUnFold){
          Column({space: 10}) {
            // 标准模式选项
            Text("标准计算器").onClick(() => {
              this.calculatorState = '标准'
              this.isUnFold = false
            })
            
            // 科学模式选项(未实现)
            Text("科学计算器").onClick(() => {
              this.calculatorState = '科学'
              this.isUnFold = false
            })
          }
        }

        // 计算结果显示区域
        Row() {
          Column() {
            Text(`${this.calculatorResultText}`)  // 显示计算结果文本
              .textAlign(TextAlign.End)          // 右对齐显示
          }
        }
      }.alignContent(Alignment.TopStart)

      /* M系列功能按钮区域 - 当前为占位实现 */
      Row() {
        ForEach(this.m, (item: string) =>{
          Column() {
            Button(item)
              .hoverEffect(HoverEffect.Scale)    // 悬停缩放效果
              .onHover((hovered: boolean) => {
                this.isHover = hovered           // 更新悬停状态
              })
          }
        })
      }
    }
    .padding(10)
  }
}

4. buttonView.ets 按钮区的代码说明

/**
 * 计算器按钮视图组件
 * @Component 标记为可复用的UI组件
 * @struct 定义组件结构
 * 
 * @State button - 按钮标签数组,包含计算器所有操作符和数字:
 *                 '%' 取模, 'CE' 清空输入, 'C' 清空, 'DE' 删除字符,
 *                 数字0-9, 运算符x-乘、-减、+加、÷除, '=' 计算结果
 * @State isHover - 按钮悬停状态标识
 * @Link res - 双向绑定的计算结果数值
 * @Link calculatorResultText - 双向绑定的计算表达式文本
 * @Consume list - 消费的历史记录列表,用于存储计算记录
 */
@Component
export default struct buttonView {
  @State button: string[] = ['%','CE','C','DE', '7', '8', '9', 'x', '4', '5', '6', '-', '1', '2', '3', '+', '0', '.', '÷','=']
  @State isHover:boolean = false
  @Link res: number
  @Link calculatorResultText: string;
  @Consume list: string[]

  /**
   * 构建计算器界面布局
   * 使用网格布局创建按钮矩阵,处理按钮交互事件
   */
  build() {
    Column() {
      Grid(){
        // 动态生成计算器按钮矩阵
        ForEach(this.button, (item: string, index: number) =>{
          GridItem() {
            Button(item)
              .fontSize(24)
              .fontColor($r('app.string.calculator_text_color'))
              .backgroundColor(this.isHover? Color.Gray : $r('app.string.calculator_bk_color'))
              .hoverEffect(HoverEffect.Scale)
              .onHover((hover: boolean) => {
                this.isHover = hover;  // 更新全局悬停状态
              })
              .onClick(() => {
                // 处理不同按钮的点击逻辑
                if (item === '=') {
                  this.calculator(this.calculatorResultText)
                }
                if(item === 'CE'){
                  this.calculatorResultText = ''
                }
                if(item === 'C'){
                  this.calculatorResultText = ""
                }
                if(item === 'DE'){
                  this.calculatorResultText = this.calculatorResultText.slice(0, -1)
                }
                // 处理数字和运算符输入
                if(item !== 'CE' && item !== 'C' && item !== 'DE' && item !== '='){
                  this.calculatorResultText += item
                }
              })
          }
          .width(80)
          .height(80)
        })
      }
      .width('100%')
      .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr')  // 8行等分布局
      .columnsTemplate('1fr 1fr 1fr 1fr')  // 4列等分布局
    }
    .width('100%')
  }

  /**
   * 计算器核心逻辑处理函数
   * @param text - 需要计算的表达式字符串
   * 功能:解析计算表达式,执行运算,更新结果和历史记录
   */
  calculator(text: string) {
    let exe: string = text;
    // 解析表达式为操作数和运算符数组
    let arr: string[] = []
    for (let i = 0; i < text.length; i++) {
      if (text[i] === '+' || text[i] === '-' || text[i] === 'x' || text[i] === '÷' || text[i] === '%') {
        arr.push(text.slice(0, i))
        arr.push(text[i])
        text = text.slice(i + 1)
        i = 0
      }
    }

    // 根据运算符执行对应计算
    if(arr[1] === '+'){
      this.res = Number(arr[0]) + Number(text)
    }
    else if(arr[1] === '-'){
      this.res = Number(arr[0]) - Number(text)
    }
    else if(arr[1] === 'x'){
      this.res = Number(arr[0]) * Number(text)
    }
    else if(arr[1] === '÷'){
      this.res = Number(arr[0]) / Number(text)
    }
    else if(arr[1] === '%'){
      this.res = Number(arr[0]) % Number(text)
    }

    // 记录完整计算表达式并更新结果
    exe += '=' + this.res
    this.calculatorResultText = this.res.toString()
    this.list.push(exe)  // 添加历史记录
  }
}

五、点赞收藏支持一下吧,代码源码私信我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2331883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qq邮箱群发程序

1.界面设计 1.1 环境配置 在外部工具位置进行配置 1.2 UI界面设计 1.2.1 进入QT的UI设计界面 在pycharm中按顺序点击&#xff0c;进入UI编辑界面&#xff1a; 点击第三步后进入QT的UI设计界面&#xff0c;通过点击按钮进行界面设计&#xff0c;设计后进行保存到当前Pycharm…

K8S学习之基础七十九:关闭istio功能

关闭istio功能 kubectl get ns --show-labels kubectl label ns default istio-injection-有istio-injectionenabled的命名空间&#xff0c;pod都会开启istio功能 反之&#xff0c;如果要开启istio&#xff0c;在对应命名空间打上该标签即可

上门预约洗鞋店小程序都具备哪些功能?

现在大家对洗鞋子的清洗条件越来越高&#xff0c;在家里不想去&#xff0c;那就要拿去洗鞋店去洗。如果有的客户没时间去洗鞋店&#xff0c;这个时候&#xff0c;有个洗鞋店小程序就可以进行上门取件&#xff0c;帮助没时间的客户去取需要清洗的鞋子&#xff0c;这样岂不是既帮…

蓝桥杯——走迷宫(Java-BFS)

这是一个经典的BFS算法 1. BFS算法保证最短路径 核心机制&#xff1a;广度优先搜索按层遍历所有可能的路径&#xff0c;首次到达终点的路径长度即为最短步数。这是BFS的核心优势。队列的作用&#xff1a;通过队列按先进先出的顺序处理节点&#xff0c;确保每一步探索的都是当…

下载firefox.tar.xz后如何将其加入到Gnome启动器

起因&#xff1a;近期&#xff08;2025-04-07&#xff09;发现firefox公布了130.0 版本&#xff0c;可以对pdf文档进行签名了&#xff0c;想试一下&#xff0c;所以卸载了我的Debian12上的firefox-esr,直接下载了新版本的tar.xz 包。 经过一番摸索&#xff0c;实现了将其加入Gn…

加密≠安全:文件夹密码遗忘背后的数据丢失风险与应对

在数字化时代&#xff0c;保护个人隐私和数据安全变得尤为重要。许多人选择对重要文件夹进行加密&#xff0c;以防止未经授权的访问。然而&#xff0c;一个常见且令人头疼的问题也随之而来——文件夹加密密码遗忘。当你突然发现自己无法访问那些加密的文件夹时&#xff0c;那种…

【开源宝藏】30天学会CSS - DAY12 第十二课 从左向右填充的文字标题动画

用伪元素搞定文字填充动效&#xff1a;一行 JS 不写&#xff0c;效果炸裂 你是否曾经在设计页面标题时&#xff0c;觉得纯文字太寡淡&#xff1f;或者想做一个有动感的文字特效&#xff0c;但又不想引入 JS 甚至 SVG&#xff1f; 在这篇文章中&#xff0c;我们将通过 一段不到…

nginx或tengine服务器,配置HTTPS下使用WebSocket的线上环境实践!

问题描述&#xff1a; HTTPS 下发起WS连接&#xff0c;连接失败&#xff0c;Chrom 浏览器报错。 socket.js:19 Mixed Content: The page at https://app.XXX.com was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint ws://172.16.10.80:903…

【Oracle篇】跨字符集迁移:基于数据泵的ZHS16GBK转AL32UTF8全流程迁移

&#x1f4ab;《博主主页》&#xff1a;奈斯DB-CSDN博客 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(MongoDB)有了解 &#x1f496;如果觉得文章对你有所帮…

西门子S7-1200PLC 工艺指令PID_Temp进行控温

1.硬件需求&#xff1a; 西门子PLC&#xff1a;CPU 1215C DC/DC/DC PLC模块&#xff1a;SM 1231 TC模块 个人电脑&#xff1a;已安装TIA Portal V17软件 加热套&#xff1a;带加热电源线以及K型热电偶插头 固态继电器&#xff1a;恩爵 RT-SSK4A2032-08S-F 其他&#xff1…

vant4+vue3上传一个pdf文件并实现pdf的预览。使用插件pdf.js

注意下载的插件的版本"pdfjs-dist": "^2.2.228", npm i pdfjs-dist2.2.228 然后封装一个pdf的遮罩。因为pdf文件有多页&#xff0c;所以我用了swiper轮播的形式展示。因为用到移动端&#xff0c;手动滑动页面这样比点下一页下一页的方便多了。 直接贴代码…

2025 数字中国创新大赛数字安全赛道数据安全产业积分争夺赛初赛-东部赛区WriteUp

2025 数字中国创新大赛数字安全赛道数据安全产业积分争夺赛初赛-东部赛区WriteUp 数据安全:ez_upload(60分)&#xff1a; 模型安全&#xff1a;数据分析&#xff1a;溯源与取证&#xff1a;1-1&#xff1a;1-2&#xff1a; 数据社工&#xff1a;2-2:2-3:2-4: 数据跨境&#xff…

2025 年网络安全终极指南

我们生活在一个科技已成为日常生活不可分割的一部分的时代。对数字世界的依赖性日益增强的也带来了更大的网络风险。 网络安全并不是IT专家的专属特权&#xff0c;而是所有用户的共同责任。通过简单的行动&#xff0c;我们可以保护我们的数据、隐私和财务&#xff0c;降低成为…

1.6-抓包技术(Burp Suite\Yakit抓包\Web、APP、小程序)

1.6-抓包技术&#xff08;Burp Suite\Yakit抓包\Web、APP、小程序&#xff09; 如果要使用抓包软件&#xff0c;基本上第一步都是要安装证书的。原因如下&#xff1a; 客户端&#xff08;浏览器或应用&#xff09;会检测到证书不受信任&#xff0c;并弹出 证书错误&#xff0…

图解力扣回溯及剪枝问题的模板应用

文章目录 选哪个的问题17. 电话号码的字母组合题目描述解题代码图解复杂度 选不选的问题78. 子集题目描述解题代码图解复杂度 两相转化77. 组合题目描述解题代码法一&#xff1a;按选哪个的思路法二&#xff1a;按选不选的思路 图解选哪个&#xff1a;选不选 复杂度 选哪个的问…

Elasticsearch 8.X 如何利用嵌入向量提升搜索能力?

众所周知&#xff0c;Elasticsearch 是一个非常流行的搜索引擎&#xff0c;因为它速度快、扩展性强&#xff0c;尤其擅长全文搜索。 近两年&#xff0c;向量嵌入&#xff08;Vector Embedding&#xff09;技术的引入&#xff0c;让 Elasticsearch 在处理高级搜索场景时变得更强…

MySQL体系架构(一)

1.1.MySQL的分支与变种 MySQL变种有好几个,主要有三个久经考验的主流变种:Percona Server,MariaDB和 Drizzle。它们都有活跃的用户社区和一些商业支持,均由独立的服务供应商支持。同时还有几个优秀的开源关系数据库,值得我们了解一下。 1.1.1.Drizzle Drizzle是真正的M…

深度强化学习基础 0:通用学习方法

过去自己学习深度强化学习的痛点&#xff1a; 只能看到各种术语、数学公式勉强看懂&#xff0c;没有建立清晰且准确关联 多变量交互关系浮于表面&#xff0c;有时候连环境、代理控制的变量都混淆 模型种类繁多&#xff0c;概念繁杂难整合、对比或复用&#xff0c;无框架分析所…

虚幻5的C++调试踩坑

本地调试VS附加调试 踩坑1 预编译版本的UE5没有符号文件&#xff0c;无法调试源码 官方代码调试所需要的符号文件bdp需要下载导入。我安装的5.5.4是预编译版本&#xff0c;并非ue5源码。所以不含bdp文件。需要调试官方代码则需要通过EPIC中下载安装。右键UE版本&#xff0c;打…

通信协议详解(十):PSI5 —— 汽车安全传感器的“抗干扰狙击手”

一、PSI5是什么&#xff1f; 一句话秒懂 PSI5就像传感器界的“防弹信使”&#xff1a;在汽车安全系统&#xff08;如气囊&#xff09;中&#xff0c;用两根线同时完成供电数据传输&#xff0c;即便车祸时线路受损&#xff0c;仍能确保关键信号准确送达&#xff01; 基础概念…