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

还在为组件状态混乱、页面跳转丢参数而头疼?

这篇博客将揭秘如何用鸿蒙ArkTS打造一个漂亮美观的智能计算器:

✅ 输入完整表达式,秒出结果------字符串切割简单计算

✅ 状态管理黑科技------@Provide/@Consume 实现跨组件实时响应

✅ 路由传参实战------历史记录页面跳转不丢数据,跨页面传参

✅ 代码即设计稿------ArkTS声明式UI开发,代码比PPT更直观

快捷跳转到想看的地方

一、项目展示

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

二、技术栈讲解

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

三、项目结构说明

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

四、核心代码实现

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

ts 复制代码
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%')
  }
}
ts 复制代码
// 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 展示区的实现

ts 复制代码
/**
 * 计算器结果视图组件
 * 
 * @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 按钮区的代码说明

ts 复制代码
/**
 * 计算器按钮视图组件
 * @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)  // 添加历史记录
  }
}

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

相关推荐
刘龙超13 分钟前
如何应对 Android 面试官 -> 电量如何优化?
android·java
CYRUS_STUDIO18 分钟前
Android NDK 编译 so 文件 抹除导出符号 反逆向
android·安全·逆向
江拥羡橙19 分钟前
2025年,HarmonyOS认证学习及考试
学习·华为·harmonyos·鸿蒙·华为证书·harmonyos认证·华为证书考试
tangweiguo030519871 小时前
Kotlin 集合过滤全指南:all、any、filter 及高级用法
android·kotlin
_一条咸鱼_1 小时前
大厂Android面试秘籍:Activity 配置变更处理(十)
android·面试·kotlin
怒放的生命.1 小时前
《MySQL从入门到精通》
android·数据库·mysql
南国樗里疾1 小时前
AndroidStudio编译报错 Duplicate class kotlin
android·kotlin
V少年2 小时前
深入浅出Java算法 排序与搜索
android
V少年2 小时前
深入浅出Java算法树结构
android
V少年2 小时前
深入浅出Java算法:数组与链表处理类问题
android