HarmonyOS实战开发-如何实现一个支持加减乘除混合运算的计算器。

介绍

本篇Codelab基于基础组件、容器组件,实现一个支持加减乘除混合运算的计算器。

说明: 由于数字都是双精度浮点数,在计算机中是二进制存储数据的,因此小数和非安全整数(超过整数的安全范围[-Math.pow(2, 53),Math.pow(2, 53)]的数据)在计算过程中会存在精度丢失的情况。

1、小数运算时:“0.2 + 2.22 = 2.4200000000000004”,当前示例的解决方法是将小数扩展到整数进行计算,计算完成之后再将结果缩小,计算过程为“(0.2 * 100 + 2.22 * 100) / 100 = 2.42”。

2、非安全整数运算时:“9007199254740992 + 1 = 9.007199254740992”,当前示例中将长度超过15位的数字转换成科学计数法,计算结果为“9007199254740992 + 1 = 9.007199254740993e15”。

相关概念

  • ForEach组件:循环渲染组件**,**迭代数组并为每个数组项创建相应的组件。
  • TextInput组件:单行文本输入框组件。
  • Image组件:图片组件,支持本地图片和网络图片的渲染展示。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  1. 完成DevEco Device Tool的安装
  2. 完成RK3568开发板的烧录

3.搭建开发环境。

  1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
  3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解。

├──entry/src/main/ets	                   // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets            // 公共常量类
│  │  └──util
│  │     ├──CalculateUtil.ets              // 计算工具类
│  │     ├──CheckEmptyUtil.ets             // 非空判断工具类
│  │     └──Logger.ets                     // 日志管理工具类
│  ├──entryability
│  │  └──EntryAbility.ts	               // 程序入口类
│  ├──model
│  │  └──CalculateModel.ets                // 计算器页面数据处理类
│  ├──pages
│  │  └──HomePage.ets                      // 计算器页面
│  └──viewmodel    
│     ├──PressKeysItem.ets                 // 按键信息类
│     └──PresskeysViewModel.ets            // 计算器页面键盘数据
└──entry/src/main/resource                 // 应用静态资源目录

页面设计

页面由表达式输入框、结果输出框、键盘输入区域三部分组成,效果图如图:

表达式输入框位于页面最上方,使用TextInput组件实时显示键盘输入的数据,默认字体大小为“64fp”,当表达式输入框中数据长度大于9时,字体大小为“32fp”。

// HomePage.ets
Column() {TextInput({ text: this.model.resultFormat(this.inputValue) }).height(CommonConstants.FULL_PERCENT).fontSize((this.inputValue.length > CommonConstants.INPUT_LENGTH_MAX ?$r('app.float.font_size_text')) : $r('app.float.font_size_input')).enabled(false).fontColor(Color.Black).textAlign(TextAlign.End).backgroundColor($r('app.color.input_back_color'))
}
....
.margin({right: $r('app.float.input_margin_right'),top: $r('app.float.input_margin_top')
})

结果输出框位于表达式输入框下方,使用Text组件实时显示计算结果和“错误”提示,当表达式输入框最后一位为运算符时结果输出框中值不变。

// HomePage.ets
Column() {Text(this.model.resultFormat(this.calValue)).fontSize($r('app.float.font_size_text')).fontColor($r('app.color.text_color'))
}
.width(CommonConstants.FULL_PERCENT)
.height($r('app.float.text_height'))
.alignItems(HorizontalAlign.End)
.margin({right: $r('app.float.text_margin_right'),bottom: $r('app.float.text_margin_bottom')})

用ForEach组件渲染键盘输入区域,其中0~9、“.”、“%”用Text组件渲染;“+-×÷=”、清零、删除用Image组件渲染。

// HomePage.ets
ForEach(columnItem, (keyItem: PressKeysItem, keyItemIndex?: number) => {Column() {Column() {if (keyItem.flag === 0) {Image(keyItem.source !== undefined ? keyItem.source : '').width(keyItem.width).height(keyItem.height)} else {Text(keyItem.value).fontSize((keyItem.value === CommonConstants.DOTS) ?$r('app.float.font_size_dot') : $r('app.float.font_size_text')).width(keyItem.width).height(keyItem.height)}}.width($r('app.float.key_width')).height(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ?$r('app.float.equals_height') : $r('app.float.key_height'))....backgroundColor(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ?$r('app.color.equals_back_color') : Color.White)...}.layoutWeight(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ? CommonConstants.TWO : 1)...
}, (keyItem: PressKeysItem) => JSON.stringify(keyItem))

组装计算表达式

页面中数字输入和运算符输入分别调用inputNumber方法和inputSymbol方法。

// HomePage.ets
ForEach(columnItem, (keyItem: PressKeysItem, keyItemIndex?: number) => {Column() {Column() {...}....onClick(() => {if (keyItem.flag === 0) {this.model.inputSymbol(keyItem.value);} else {this.model.inputNumber(keyItem.value);}})}...)...
}, (keyItem: PressKeysItem) => JSON.stringify(keyItem))

说明: 输入的数字和运算符保存在数组中,数组通过“+-×÷”运算符将数字分开。 例如表达式为“10×8.2+40%÷2×-5-1”在数组中为["10", "×", "8.2", "+", "40%", "÷", "2", "×", "-5", "-", "1"]。 表达式中“%”为百分比,例如“40%”为“0.4”。

当为数字输入时,首先根据表达式数组中最后一个元素判断当前输入是否匹配,再判断表达式数组中最后一个元素为是否为负数。

// CalculateModel.ets
inputNumber(value: string) {...let len = this.expressions.length;let last = len > 0 ? this.expressions[len - 1] : '';let secondLast = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined;if (!this.validateEnter(last, value)) {return;}if (!last) {this.expressions.push(value);} else if (!secondLast) {this.expressions[len - 1] += value;}if (secondLast && CalculateUtil.isSymbol(secondLast)) {this.expressions[len -1] += value;}if (secondLast && !CalculateUtil.isSymbol(secondLast)) {this.expressions.push(value);}...
}// CalculateModel.ets
validateEnter(last: string, value: string) {if (!last && value === CommonConstants.PERCENT_SIGN) {return false;}if ((last === CommonConstants.MIN) && (value === CommonConstants.PERCENT_SIGN)) {return false;}if (last.endsWith(CommonConstants.PERCENT_SIGN)) {return false;}if ((last.indexOf(CommonConstants.DOTS) !== -1) && (value === CommonConstants.DOTS)) {return false;}if ((last === '0') && (value != CommonConstants.DOTS) &&(value !== CommonConstants.PERCENT_SIGN)) {return false;}return true;
}

当输入为“=”运算符时,将结果输入出框中的值显示到表达式输入框中,并清空结果输出框。当输入为“清零”运算符时,将页面和表达式数组清空。

// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {case Symbol.CLEAN:this.expressions = [];this.context.calValue = '';break;...case Symbol.EQU:if (len === 0) {return;}this.getResult().then(result => {if (!result) {return;}this.context.inputValue = this.context.calValue;this.context.calValue = '';this.expressions = [];this.expressions.push(this.context.inputValue);})break;...}...
}

当输入为“删除”运算符时,若表达式数组中最后一位元素为运算符则删除,为数字则删除数字最后一位,重新计算表达式的值(表达式数组中最后一位为运算符则不参与计算),删除之后若表达式长度为0则清空页面。

// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {...case CommonConstants.SYMBOL.DEL:this.inputDelete(len);break;...}...
}// CalculateModel.ets
inputDelete(len: number) {if (len === 0) {return;}let last = this.expressions[len - 1];let lastLen = last.length;if (lastLen === 1) {this.expressions.pop();len = this.expressions.length;} else {this.expressions[len - 1] = last.slice(0, last.length - 1);}if (len === 0) {this.context.inputValue = '';this.context.calValue = '';return;}if (!CalculateUtil.isSymbol(this.expressions[len - 1])) {this.getResult();}
}

当输入为“+-×÷”四则运算符时,由于可输入负数,故优先级高的运算符“×÷”后可输入“-”,其它场景则替换原有运算符。

// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {...default:this.inputOperators(len, value);break;}...
}// CalculateModel.ets
inputOperators(len: number, value: string) {let last = len > 0 ? this.expressions[len - 1] : undefined;let secondLast = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined;if (!last && (value === Symbol.MIN)) {this.expressions.push(this.getSymbol(value));return;}if (!last) {return;}if (!CalculateUtil.isSymbol(last)) {this.expressions.push(this.getSymbol(value));return;}if ((value === Symbol.MIN) &&(last === CommonConstants.MIN || last === CommonConstants.ADD)) {this.expressions.pop();this.expressions.push(this.getSymbol(value));return;}if (!secondLast) {return;}if (value !== Symbol.MIN) {this.expressions.pop();}if (CalculateUtil.isSymbol(secondLast)) {this.expressions.pop();}this.expressions.push(this.getSymbol(value));
}

解析计算表达式

将表达式数组中带“%”的元素转换成小数,若表达式数组中最后一位为“+-×÷”则删除。

// CalculateUtil.ets
parseExpression(expressions: Array<string>): string {...let len = expressions.length;...expressions.forEach((item: string, index: number) => {// 处理表达式中的%if (item.indexOf(CommonConstants.PERCENT_SIGN) !== -1) {expressions[index] = (this.mulOrDiv(item.slice(0, item.length - 1),CommonConstants.ONE_HUNDRED, CommonConstants.DIV)).toString();}// 最后一位是否为运算符if ((index === len - 1) && this.isSymbol(item)) {expressions.pop();}});...
}

先初始化队列和栈,再从表达式数组左边取出元素,进行如下操作:

  • 当取出的元素为数字时则放入队列中。
  • 当取出的元素为运算符时,先判断栈中元素是否为空,是则将运算符放入栈中,否则判断此运算符与栈中最后一个元素的优先级,若此运算符优先级小则将栈中最后一个元素弹出并放入队列中,再将此运算符放入栈中,否则将此运算符放入栈中。
  • 最后将栈中的元素依次弹出放入队列中。
// CalculateUtil.ets
parseExpression(expressions: Array<string>): string {...while (expressions.length > 0) {let current = expressions.shift();if (current !== undefined) {if (this.isSymbol(current)) {while (outputStack.length > 0 &&this.comparePriority(current, outputStack[outputStack.length - 1])) {let popValue: string | undefined = outputStack.pop();if (popValue !== undefined) {outputQueue.push(popValue);}}outputStack.push(current);} else {outputQueue.push(current);}}}while (outputStack.length > 0) {outputQueue.push(outputStack.pop());}...
}

以表达式“3×5+4÷2”为例,用原理图讲解上面代码,原理图如图:

遍历队列中的元素,当为数字时将元素压入栈,当为运算符时将数字弹出栈,并结合当前运算符进行计算,再将计算的结果压栈,最终栈底元素为表达式结果。

// CalculateUtil.ets
dealQueue(queue: Array<string>) {...let outputStack: string[] = [];while (queue.length > 0) {let current: string | undefined = queue.shift();if (current !== undefined) {if (!this.isSymbol(current)) {outputStack.push(current);} else {let second: string | undefined = outputStack.pop();let first: string | undefined = outputStack.pop();if (first !== undefined && second !== undefined) {let calResultValue: string = this.calResult(first, second, current)outputStack.push(calResultValue);}}}}if (outputStack.length !== 1) {return 'NaN';} else {let end = outputStack[0].endsWith(CommonConstants.DOTS) ?outputStack[0].substring(0,  outputStack[0].length - 1) : outputStack[0];return end;}
}

获取表达式“3×5+4÷2”组装后的表达式,用原理图讲解上面代码,原理图如图:

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. ForEach组件的使用。
  2. TextInput组件的使用。
  3. Image组件的使用。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频》

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》

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

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

相关文章

【详解】运算放大器工作原理及其在信号处理中的核心作用

什么是运算放大器 运算放大器&#xff08;简称“运放”&#xff09;是一种放大倍数非常高的电路单元。在实际电路中&#xff0c;它常常与反馈网络一起组成一定的功能模块。它是一种带有特殊耦合电路和反馈的放大器。输出信号可以是输入信号的加法、减法、微分和积分等数学运算…

vue3+ts项目 | axios 的测试 | 测试接口

在 App.vue 中&#xff0c;测试接口 // 测试接口import request from /utils/request;import { onMounted } from vue;onMounted(() > {request.get(/hosp/hospital/1/10).then((res) > {console.log("APP组件展示获取的数据",res);})}) 在request.ts中&…

link 样式表是否会阻塞页面内容的展示?取决于浏览器,edge 和 chrome 会,但 firefox 不会。

经过实测&#xff1a; 在 head 中 link 一个 1M 大小的样式表。设置网络下载时间大概为 10 秒。 edge 和 chrome 只有在下载完样式表后&#xff0c;页面上才会出现内容。而 firefox 可以直接先显示内容&#xff0c;然后等待样式表下载完成后再应用样式。 DOMContentLoaded 事…

Vue指令之v-for

跟其他语言中的for一样&#xff0c;是用来渲染多个类似实例的。 语法为v-for"(item, index) in 可迭代对象"&#xff0c;一般就用于遍历数组。这里的语法跟Python中的for循环enumerate也有点相似之处&#xff0c;但要注意item在前&#xff0c;index在后&#xff0c;…

复杂度3 二分查找函数

文章预览&#xff1a; 题目算法代码 题目 算法 本题要求用二分法查找顺序表的一个值&#xff0c;比较简单注意指针格式即可 代码 Position BinarySearch( List L, ElementType X ) {int begin1,mid;int endL->Last;ElementType temp;while(begin<end){mid(beginend)/2…

【Spring Security】 快速入门

文章目录 一、 身份认证Demo1、创建工程2、代码编写2.1、Controller2.2、Html2.3、application.properties配置 3、启动项目并访问 二、Spring Security 默认做了什么二、底层原理1.概述2.FiltersDelegatingFilterProxyFilterChainProxySecurityFilterChainSecurity Filters 三…

vue3-pinia使用(末尾有彩蛋)

什么是 pinia Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 之前用的是 vuex&#xff0c;后面 vue 官方团队不维护了&#xff0c;推荐使用 pinia 安装 yarn add pinia # 或者使用 npm npm install piniapnpm install piniaStore 是什么&#xf…

实验一 Python集成开发环境的搭建及可视化库的安装

一、安装集成开发环境 下载安装包 官方网址&#xff1a; Free Download | Anaconda 或者镜像网站下载&#xff08;较快&#xff09; https://repo.anaconda.com/archive/ 安装 配置环境变量 验证 输入&#xff1a; conda -V 二、下载pyecharts环境 点击 Anaconda Promp…

spring-boot之shiro安全框架配置使用

shiro架构&#xff08;外部&#xff09; shiro架构(内部) 具体API操作 获取当前的用户对象 Subject currentUser SecurityUtils.getSubject();通过当前用户拿到session Session session currentUser.getSession(); session.setAttribute("someKey", "aValu…

微信公众号运营必备工具合集

微信公众号运营必备工具合集 各位同学&#xff0c;想要成为一名合格的公众号运营&#xff0c;必须要搭建一个属于自己的运营工具库&#xff0c;可以在日常工作中最大限度的提高效率。 91微信编辑器 &#xff1a;http://bj.91join.com/ 壹伴助手&#xff1a;https://yiban.io…

下载及安装PHP,composer,phpstudy,thinkPHP6.0框架

文章目录 前言 thinkPHP是一款开源的PHP框架&#xff0c;它是基于MVC&#xff08;Model-View-Controller&#xff09;设计模式构建的。thinkPHP提供了丰富的功能和组件&#xff0c;使得开发人员可以快速、高效地构建和维护Web应用程序。 以下是thinkPHP框架的一些特点和功能&…

上位机图像处理和嵌入式模块部署(qmacvisual测量标定)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 在机器视觉中,测量是很重要的一个环节。如果是简单的定位,可能精度要求并不那么严格。但是如果是对产品进行QA测量,需要精确到0.1mm,甚至是0.05mm这样的精度,那就需要对camera…

蓝队面经(一)

蓝队面经(一) 文章目录 蓝队面经(一)入侵排查思路windows入侵排查思路Linux入侵排查思路 Linux 如何查看登录日志Windows 和 Linux 的日志文件放在哪里&#xff1f;WindowsLinux Linux 常用排查命令有哪些&#xff1f;Linux 的 Selinux 是什么&#xff1f;如何设置 Selinux&…

【Leetcode】2952. 需要添加的硬币的最小数量

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个下标从 0 0 0 开始的整数数组 c o i n s coins coins&#xff0c;表示可用的硬币的面值&#xff0c;以及一个整数 t a r g e t target target 。 如果存在某个 c o i …

剑指Offer题目笔记21(计数排序)

面试题74&#xff1a; 问题&#xff1a; ​ 输入一个区间的集合&#xff0c;将重叠的区间合并。 解决方案&#xff1a; ​ 先将所有区间按照起始位置排序&#xff0c;然后比较相邻两个区间的结束位置就能知道它们是否重叠。如果它们重叠就将它们合并&#xff0c;然后判断合并…

精选2024年最佳项目管理系统!实用推荐与详细评测

随着企业规模的扩大&#xff0c;项目量也会呈几何倍的增长&#xff0c;项目管理系统就成了企业管理必不可少的一部分。2024年优秀的项目管理系统推荐。今年为大家带来Microsoft Project、Zoho Projects、Jira以及Wrike项目管理系统评测。 什么是项目管理系统&#xff1f; 项目…

怎么更新sd-webui AUTOMATIC1111/stable-diffusion-webui ?

整个工程依靠脚本起来的&#xff1a; 可直接到stable-diffusion-webui子目录执行&#xff1a; git pull更新代码完毕后&#xff0c;删除venv的虚拟环境。 然后再次执行webui.sh&#xff0c;这样会自动重新启动stable-diffusion-webui.

springboot心灵治愈交流平台

摘 要 本论文主要论述了如何使用JAVA语言开发一个心灵治愈交流平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述心灵治愈交流平台的当前背景以及系统开发的…

啥是MCU,MCU科普

啥是MCU&#xff0c;MCU科普 附赠自动驾驶学习资料和量产经验&#xff1a;链接 MCU是Microcontroller Unit 的简称&#xff0c;中文叫微控制器&#xff0c;俗称单片机&#xff0c;是把CPU的频率与规格做适当缩减&#xff0c;并将内存、计数器、USB、A/D转换、UART、PLC、DMA等…

Leetcode 42.接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图&#x…