基于鸿蒙next实现一个可以进行加减乘除的简单计算器。
环境配置:DevEco Studio NEXT 5.0.2 Api14
功能实现:
支持基本运算:加(+)、减(-)、乘(×)、除(÷)
支持连续运算(如3 + 5 - 2)
支持小数点输入
支持清除功能(C)
支持结果显示(=)
支持负号输入(如-6+3= -3)
案例效果:
一、自定义构建组件实现计算器输入按钮绘制和界面的绘制
//静态界面界面绘制
build() {
Column({ space: 8 }) {
// 显示区域
Column({ space: 4 }) {
Text(this.expression)
.fontSize(24)
.textAlign(TextAlign.End)
.width('90%')
.fontColor('#666666')
.height('3%')
Text(this.displayText)
.fontSize(48)
.textAlign(TextAlign.End)
.width('90%')
.height('18%')
}
.margin({ bottom: 20 })
Blank()
// 按钮区域
Grid() {
GridItem() { this.createButton('C', '#ff9999') }
GridItem() { this.createButton('±', '#cccccc') }
GridItem() { this.createButton('%', '#cccccc') }
GridItem() { this.createButton('÷', '#ff9933') }
GridItem() { this.createButton('7') }
GridItem() { this.createButton('8') }
GridItem() { this.createButton('9') }
GridItem() { this.createButton('×', '#ff9933') }
GridItem() { this.createButton('4') }
GridItem() { this.createButton('5') }
GridItem() { this.createButton('6') }
GridItem() { this.createButton('-', '#ff9933') }
GridItem() { this.createButton('1') }
GridItem() { this.createButton('2') }
GridItem() { this.createButton('3') }
GridItem() { this.createButton('+', '#ff9933') }
GridItem() { this.createButton('0','#ff1463db') }.columnStart(0).columnEnd(1)
GridItem() { this.createButton('.') }
GridItem() { this.createButton('=', '#ff9933') }
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsGap(8)
.columnsGap(8)
.width('90%')
.height('100%')
}
.width('100%')
.height('100%')
.padding(10)
.backgroundColor('#f0f0f0')
}
//自定义按钮按钮
@Builder
createButton(text: string, bgColor: string = '#e6e6e6', colspan: number = 1) {
Button(text) {
Text(text)
.fontSize(32)
.fontColor(text === 'C' ? '#ff4444' : '#333333')
}
.onClick(() => this.handleButtonClick(text))
.backgroundColor(bgColor)
.width(colspan === 2 ? '200%' : '100%')
.height(80)
}
二、处理按钮的点击跳转至不同函数
handleButtonClick(buttonValue: string) {
if (this.isNumber(buttonValue)) {
this.handleNumber(buttonValue)
} else if (buttonValue === '.') {
this.handleDecimal()
} else if (this.isOperator(buttonValue)) {
this.handleOperator(buttonValue)
} else if (buttonValue === '=') {
this.calculate()
} else if (buttonValue === 'C') {
this.clear()
} else if (buttonValue === '±') {
this.handleSignChange()
}
}
//判断输入是否未数字或则运算符
private isNumber(value: string): boolean {
return /^-?\d+\.?\d*$/.test(value) // 支持负数
}
private isOperator(value: string): boolean {
return ['+', '-', '×', '÷'].includes(value)
}
三、处理数字、小数点 、等于号、清除号、负号和操作符按钮的点击
private handleNumber(num: string) {
if (this.currentNumber === '0') {
this.currentNumber = num
} else {
this.currentNumber += num
}
this.displayText = this.currentNumber
}
private handleDecimal() {
if (!this.currentNumber.includes('.')) {
this.currentNumber += this.currentNumber === '' ? '0.' : '.'
this.displayText = this.currentNumber
}
}
private handleOperator(op: string) {
if (this.currentNumber !== '') {
this.expression += ` ${this.currentNumber} ${op}`
this.currentNumber = ''
this.displayText = '0'
}
}
private calculate() {
if (this.currentNumber === '') return
const fullExpression = `${this.expression} ${this.currentNumber}`
try {
const result = this.evaluateExpression(fullExpression)
this.displayText = result.toString()
this.expression = `${fullExpression} =`
this.currentNumber = result.toString()
} catch (error) {
this.displayText = 'Error'
this.expression = ''
this.currentNumber = '0'
}
}
private clear() {
this.currentNumber = '0'
this.expression = ''
this.displayText = '0'
}
private handleSignChange() {
if (this.currentNumber !== '') {
if (this.currentNumber.startsWith('-')) {
this.currentNumber = this.currentNumber.substring(1)
} else {
this.currentNumber = `-${this.currentNumber}`
}
this.displayText = this.currentNumber
} else if (this.displayText !== '0') {
if (this.displayText.startsWith('-')) {
this.displayText = this.displayText.substring(1)
} else {
this.displayText = `-${this.displayText}`
}
this.currentNumber = this.displayText
}
}
四、核心计算逻辑(支持运算符优先级)
tokenize 函数的核心功能是将一个数学表达式字符串拆分为 token 数组,支持处理数字、运算符、负数和空格。
private tokenize(expr: string): string[] {
const tokens: string[] = []
let currentToken = ''
for (let i = 0; i < expr.length; i++) {
const char = expr[i]
if (this.isOperator(char) || char === ' ') {
if (currentToken !== '') {
tokens.push(currentToken)
currentToken = ''
}
if (char !== ' ') {
// 处理负数:如果当前字符是'-',且前一个字符是操作符或空格,或者当前是第一个字符
if (char === '-' && (i === 0 || this.isOperator(expr[i - 1]) || expr[i - 1] === ' ')) {
currentToken += char // 将'-'作为负数的一部分
} else {
tokens.push(char)
}
}
} else {
currentToken += char
}
}
if (currentToken !== '') {
tokens.push(currentToken)
}
return tokens
}
//解析并计算一个数学表达式的值。它使用了经典的 Shunting-yard 算法 将中缀表达式
//(例如 3 + 5 × 2)转换为后缀表达式(例如 3 5 2 × +),然后通过栈来计算后缀表达式的值。
private evaluateExpression(expr: string): number {
const tokens = this.tokenize(expr)
const outputQueue: string[] = []
const operatorStack: string[] = []
// 定义运算符优先级
const precedence: Record<string, number> = {
'+': 1,
'-': 1,
'×': 2,
'÷': 2
}
// 中缀转后缀(Shunting-yard算法)
tokens.forEach(token => {
if (this.isNumber(token) || (token.startsWith('-') && token.length > 1)) {
// 如果是数字或负数,直接加入输出队列
outputQueue.push(token)
} else if (this.isOperator(token)) {
while (operatorStack.length > 0 &&
precedence[operatorStack[operatorStack.length - 1]] >= precedence[token]) {
outputQueue.push(operatorStack.pop()!)
}
operatorStack.push(token)
}
})
while (operatorStack.length > 0) {
outputQueue.push(operatorStack.pop()!)
}
// 计算后缀表达式
const stack: number[] = []
outputQueue.forEach(token => {
if (this.isNumber(token) || (token.startsWith('-') && token.length > 1)) {
// 如果是数字或负数,解析为数值并压入栈
stack.push(parseFloat(token))
} else {
const b = stack.pop()!
const a = stack.pop()!
switch (token) {
case '+': stack.push(a + b); break
case '-': stack.push(a - b); break
case '×': stack.push(a * b); break
case '÷':
if (b === 0) throw new Error('Division by zero')
stack.push(a / b)
break
}
}
})
return stack[0]
}
这个函数是计算器的核心逻辑,能够正确处理运算符优先级、负数和错误情况(如除零)。如果需要扩展功能(例如支持更多运算符或函数),可以在此基础上进行修改。
evaluateExpression 函数通过以下步骤实现表达式的计算:
将中缀表达式转换为后缀表达式(Shunting-yard 算法)。
使用栈计算后缀表达式的值。
返回最终的计算结果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。