目录
1. 在index.ets文件中通过 @Extend(Button) 装饰器扩展Button 组件设置按钮样式函数myButton
3. 设置获取运算符优先级的函数 getOperatorPrecedence
4.设置计算表达式结果的函数 calculateResult
🎯学习小目标:
- 创建一个工程,在index.ets文件中进行编辑
- 使用按钮组件设计计算器的布局
- 点击按钮后将计算过程显示在Text组件中
- 点击等号显示计算结果
📺演示效果:
图1 预览器演示效果
📖实验步骤及方法:
1. 在index.ets文件中通过 @Extend(Button) 装饰器扩展Button 组件设置按钮样式函数myButton
@Extend(Button) function myButton() { .type(ButtonType.Normal) .width("270px") .height("270px") .backgroundColor(Color.Black) .border({ color: Color.White, style: BorderStyle.Solid, width: 2 }) .fontSize(40) .fontColor(Color.White) .fontWeight(800) .borderRadius(0) }
2. 设置运算符枚举类型 Operator
enum Operator { PLUS = '+', MINUS = '-', TIMES = '*', DIVIDE = '/' }
3. 设置获取运算符优先级的函数 getOperatorPrecedence
function getOperatorPrecedence(operator: string): number { switch (operator) { case Operator.PLUS: case Operator.MINUS: return 1; case Operator.TIMES: case Operator.DIVIDE: return 2; default: throw new Error("Invalid operator: " + operator); } }
4.设置计算表达式结果的函数 calculateResult
function calculateResult(expression: string): number { // 去除表达式两端可能存在的空白字符 expression = expression.trim(); // 使用两个栈,一个用于存储操作数,一个用于存储运算符 let operandStack: number[] = []; let operatorStack: string[] = []; // 用于临时存储数字字符组成的字符串,以便转换为数字 let tempNumber = ""; for (let i = 0; i < expression.length; i++) { let char = expression[i]; if (char >= '0' && char <= '9' || char === '.') { // 如果是数字字符或小数点,累加到临时数字字符串中 tempNumber += char; } else if (char === '+' || char === '-' || char === '*' || char === '/') { // 如果是运算符 // 先将之前临时存储的数字转换为实际数字并压入操作数栈 if (tempNumber!== "") { operandStack.push(Number(tempNumber)); tempNumber = ""; } // 处理运算符栈,按照优先级进行运算 while ( operatorStack.length > 0 && getOperatorPrecedence(operatorStack[operatorStack.length - 1]) >= getOperatorPrecedence(char) ) { let operator = operatorStack.pop(); let operand2 = operandStack.pop(); + let operand1 = operandStack.pop(); // 验证弹出的操作数是否有效 if (Operand1 === undefined || operand2 === undefined) { throw new Error("Invalid operation: missing operand(s)"); } if (operator === Operator.PLUS) { operandStack.push(Operand1 + operand2); } else if (operator === Operator.MINUS) { operandStack.push(Operand1 - operand2); } else if (operator === Operator.TIMES) { operandStack.push(Operand1 * operand2); } else if (operator === Operator.DIVIDE) { operandStack.push(Operand1 / operand2); } } // 将当前运算符压入运算符栈 operatorStack.push(char); } else if (char === '(') { // 如果是左括号,直接压入运算符栈 operatorStack.push(char); } else if (char === ')') { // 如果是右括号 // 先将之前临时存储的数字转换为实际数字并压入操作数栈 if (tempNumber!== "") { operandStack.push(Number(tempNumber)); tempNumber = ""; } // 处理运算符栈,直到遇到左括号 while ( operatorStack.length > 0 && operatorStack[operatorStack.length - 1]!== '(') { let operator = operatorStack.pop(); let operand2 = operandStack.pop(); let operand1 = operandStack.pop(); // 验证弹出的操作数是否有效 if (Operand1 === undefined || operand2 === undefined) { throw new Error("Invalid operation: missing operand(s)"); } if (operator === Operator.PLUS) { operandStack.push(Operand1 + operand2); } else if (operator === Operator.MINUS) { operandStack.push(Operand1 - operand2); } else if (operator === Operator.TIMES) { operandWordStack.push(Operand1 * operand2); } else if (operator ==Operator.DIVIDE) { operandStack.push(Operand1 / operand2); } } // 弹出左括号 operatorStack.pop(); } } // 处理表达式末尾可能存在的数字 if (tempNumber!== "") { operandStack.push(Number(tempNumber)); } // 处理运算符栈中剩余的运算符 if (operatorStack.length > 0) { let operator = operatorStack.pop(); let operand2 = operandStack.pop(); let operand1 = operandStack.pop(); // 验证弹出的操作数是否有效 if (Operand1 === undefined || operand2 === undefined) { throw new Error("Invalid operation: missing operand(s)"); } if (operator === Operator.PLUS) { operandStack.push(Operand1 + operand2); } else if (operator === Operator.MINUS) { operandStack.push(Operand1 - operand2); } else if (operator === Operator.TIMES) { operandStack.push(Operand1 * operand2); } else if (operator ==Operator.DIVIDE) { operandStack.push(Operand1 / operand2); } } // 返回最终的计算结果 return operandStack[0]; }
5.设置主界面组件 Index
@Entry @Component struct Index { @State result: string = "" @State number: number = 0 build() { Column() { Row() { Text(this.result).fontSize(50) } Row() { Column() { Button("7").onClick(() => { this.result += "7" }).myButton() } Column() { Button("8").onClick(() => { this.result += "8" }).myButton() } Column() { Button("9").onClick(() => { this.result += "9" }).myButton() } Column() { Button("+").onClick(() => { this.result += "+" }).myButton() } } Row() { Column() { Button("4").onClick(() => { this.result += "4" }).myButton() } Column() { Button("5").onClick(() => { this.result += "5" }).myButton() } Column() { Button("6").onClick(() => { this.result += "6" }).myButton() } Column() { Button("-").onClick(() => { this.result += "-" }).myButton() } } Row() { Column() { Button("1").onClick(() => { this.result += "1" }).myButton() } Column() { Button("2").onClick(() => { thisresult += "2" }).myButton() } Column() { Button("3").onClick(() => { this.result += "3" }).myButton() } Column() { Button("*").onClick(() => { this.result += "*" }).myButton() } } Row() { Column() { Button("0").onClick(() => { this.result += "0" }).myButton() } Column() { Button(".").onClick(() => { this.result += "." }).myButton() } Column() { Button("=").onClick(() => { try { this.result = calculateResult(this.result).toString(); } catch (e) { this.result = "Error: " + e.message; } }).myButton() } Column() { Button("/").onClick(() => { this.result += "/" }).myButton() } } Row() { Column() { Button("C").onClick(() => { this.result = ""; this.number = 0; }).myButton() } } } } }
6.模拟机运行测试
👋实验小结
本次计算器应用开发实验主要围绕 index.ets 文件展开。利用按钮组件顺利完成布局设计,数字、运算符及功能按钮排列有序,操作逻辑清晰直观。点击事件处理精准,计算过程能实时且无误地呈现在 Text 组件,点击等号后计算函数高效解析表达式并给出准确结果,达成核心功能要求。然而,实验中也发现一些待改进之处,例如错误提示不够详尽,难以助力用户快速定位输入错误;界面美观性欠佳,颜色与样式缺乏精致感;响应式设计不足,在不同屏幕规格下适配性差。总体而言,本次实验已成功构建起基本功能框架,后续将针对上述问题深入探究优化方案,不断打磨细节,在完善计算器功能与提升用户体验的道路上持续精进,进而提升自身编程与应用开发的综合能力水平。