首页 前端知识 使用ArkTs进行计算器的实现

使用ArkTs进行计算器的实现

2024-08-21 10:08:09 前端知识 前端哥 517 243 我要收藏

使用ArkTs进行计算器的实现,基于deveco-studio3.1.1,新版未尝试

  • 1、写出大致的计算器构造
    • 在calcultorDemo.ets文件中
    • 在calculatorResult.ets中
  • 2、进行计算机的功能部分
    • 给每个按钮分别添加点击事件
    • 完成value的计算事件,利用栈进行算术表达式的求值:
  • 3、完整代码(一个文件)

1、写出大致的计算器构造

在calcultorDemo.ets文件中

import {calculatorResult} from './calculatorResult'
import numberstack  from './NumberStack'
import characterstack from './CharacterStack'
@Entry
@Component
struct calcultorDemo {
  @State value:string =''
   @State result:string=''
  @State opacityValue:number=0
  @State fontSizeValue:number=30
  numbers :string[]=['(',')','÷','×','1','2','3','-','4','5','6','+','7','8','9']
  numbers2:string[]=['%','0','.']
  build() {
    Column(){
      calculatorResult({value:$value,result:this.result,opacityValue:$opacityValue,fontSizeValue:$fontSizeValue})
      Column(){//计算机主体页面
        Grid(){
          GridItem(){
            Text('MC')
              .TextStyle()
          }
          .oneStyle()//MC(清零)工具

          GridItem(){
            Text('MR')
              .TextStyle()
          }
          .oneStyle()

          GridItem(){
            Image($r('app.media.delete'))
              .fillColor(Color.Blue)
              .height(40)
          }
          .oneStyle()
          .onClick(()=>{
            this.value=this.value.slice(0,this.value.length-1)
            this.result=''
          })

          GridItem(){
            Text('C')
              .TextStyle()
          }
          .oneStyle()
          .onClick(()=>{
            this.value=''
            this.result=''
          })

          ForEach(this.numbers,item=>{
            GridItem(){
              Text(item)
                .TextStyle()
            }
            .onClick(()=>{
              if (this.value === '' && (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%')) {
                return
              }//判断点击的第一个字符是不是运算符,若是则返回
              if (this.value[this.value.length - 1] === '+' || this.value[this.value.length - 1] === '-' ||
              this.value[this.value.length - 1] === '×' || this.value[this.value.length - 1] === '÷' ||
              this.value[this.value.length - 1] === '%') {
                // 如果当前点击的是运算符,则替换最后一个运算符
                if (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%') {
                  this.value = this.value.slice(0, this.value.length - 1) + item
                } else {
                  this.value += item
                }
              } else {
                this.value = this.value.concat(item)
              }
            })
            .oneStyle()
          })

          GridItem(){
            Text('=')
              .TextStyle()
              .fontColor(Color.White)
          }
          .rowStart(5)
          .rowEnd(6)
          .borderRadius(40)
          .backgroundColor(Color.Blue)
          .onClick(()=>{
            this.result=total(checkParentheses(this.value+'#').cleanedExpression)
            this.opacityValue=1
            this.fontSizeValue=50
          })

          ForEach(this.numbers2,item=>{
            GridItem(){
              Text(item)
                .TextStyle()
            }
            .onClick(()=>{this.value=this.value.concat(item)})
            .oneStyle()
          })
        }
        .width('100%')
        .height(500)
        .columnsTemplate('1fr 1fr 1fr 1fr')
        .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
        .columnsGap(8)
        .rowsGap(12)
        .padding(12)
        .backgroundColor('#fff6f0f0')
        .borderRadius({topLeft:20,topRight:20})
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.End)
    }
    .height('100%')
  }
}
@Styles function oneStyle(){
  .backgroundColor(Color.White)
  .height(70)
  .width(70)
  .borderRadius(40)
  .shadow({color:Color.Gray,radius:5})
}
@Extend(Text) function TextStyle() {
  .fontSize(25)
  .fontWeight(400)
}

以上代码写出计算机的键盘部分,使用Grid组件进行键盘的分隔,以及对相同功能的按钮进行ForEach循环渲染减少占有空间
在这里插入图片描述

在calculatorResult.ets中

进行键盘输入(TextInput)和输出(Text)的编写

@Component
export struct  calculatorResult{
  @Link value:string
  @Prop result:string
  @Link opacityValue:number
  @Link fontSizeValue:number
  build() {
      Column(){
        TextInput({text:this.value})
          .height(80)
          .margin({top:60})
          .placeholderFont({size:60})
          .fontSize(60)
          .fontWeight(450)
          .textAlign(TextAlign.End)
          .backgroundColor(Color.White)
        Text(this.result)
          .opacity(this.opacityValue)
          .width('100%')
          .height(60)
          .fontSize(this.fontSizeValue)
          .fontWeight(450)
          .textAlign(TextAlign.End)
          .animation({duration:500})
            // @ts-ignore
          .textOverflow(TextOverflow.Clip)
      }
  }
}

经过以上两个文件的渲染后,计算机的大概形状显示出来,如图所示:
在这里插入图片描述

2、进行计算机的功能部分

给每个按钮分别添加点击事件

1、对1,2,3,4,5,6,7,8,9,+, - , × ,÷添加存储表达式的点击事件,定义 @State value:string =‘’ 在每次进行点击时,对所点击的内容做出判断,储存所点击的内容。

.onClick(()=>{
              if (this.value === '' && (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%')) {
                return
              }//判断点击的第一个字符是不是运算符,若是则返回
              if (this.value[this.value.length - 1] === '+' || this.value[this.value.length - 1] === '-' ||
              this.value[this.value.length - 1] === '×' || this.value[this.value.length - 1] === '÷' ||
              this.value[this.value.length - 1] === '%') {
                // 如果在上一个字符是运算符时,当前点击的是运算符,则替换最后一个运算符
                if (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%') {
                  this.value = this.value.slice(0, this.value.length - 1) + item
                } else {
                //若当前点击不是运算符而是数字时,则继续输入表达式
                  this.value += item
                }
              } else {
                this.value = this.value.concat(item)
              }
            })

2、对删除按钮:定义 @State result:string=''进行计算机结果的保存,方便后续渲染在Text组件中。

.onClick(()=>{
            this.value=this.value.slice(0,this.value.length-1)
            this.result=''
          })

3、对于清零工具则将value和result都置为空

.onClick(()=>{
            this.value=''
            this.result=''
          })

完成value的计算事件,利用栈进行算术表达式的求值:

主要思路:
1、将中缀表达式转为后缀表达式;
2、对后缀表达式求值;
具体可参考:栈的应用-表达式求值

代码如下:
1、在calcultorDemo.ets中:

//检查括号是否多余,且若前括号多余则去掉多余的前括号
function checkParentheses(expression: string): { valid: boolean, cleanedExpression: string } {
  let stack = []
  let cleanedExpression = expression
//判断表达式括号是否对应
  for (let ch of expression) {
    if (ch === '(') {
      stack.push(ch)
    } else if (ch === ')') {//后括号多余则直接返回错误
      if (stack.length === 0) {
        return { valid: false, cleanedExpression: expression }
      }
      stack.pop()
    }
  }
  //若不对应,去掉多余的前括号
  while (stack.length > 0) {
    let index = cleanedExpression.indexOf('(')
    if (index !== -1) {
      cleanedExpression = cleanedExpression.slice(0, index) + cleanedExpression.slice(index + 1)
    }
    stack.pop()
  }
  console.log(cleanedExpression)
  return { valid: stack.length === 0, cleanedExpression }

}

//对表达式求值
function total(expression:string){
  characterstack.push('#')
  let i=0
  let ch=expression[i]
  while (ch!='#'||characterstack.peek()!='#') {
    if(!Instring(ch)){//进行多位数的入栈
      let numStr = ch
      while (i + 1 < expression.length && !Instring(expression[i + 1])) {
        ch = expression[++i]
        numStr += ch
      }
      numberstack.push(numStr)
      ch = expression[++i]
    }
    else{

      switch (Precede(characterstack.peek(),ch)){
        case '<':{
          characterstack.push(ch);
          ch=expression[++i]
          break
        }
        case '>':{
          let theta= characterstack.pop()
          let b=numberstack.pop()
          let a=numberstack.pop()
          numberstack.push(Operate(a,theta,b))
          break
        }
        case '=':{
          characterstack.pop()
          ch=expression[++i]
          break
        }
      }
    }
  }
  return numberstack.peek()
}


//判断ch是否为运算符
function Instring(ch:string){
  let num:string[]=['+','-','(',')','÷','×','#','%']
  return num.includes(ch)
}


//判断运算符的优先级
function Precede(thetal:string,thetal2:string):string{
  if((thetal=='('&&thetal2==')')||(thetal=='#'&&thetal2=='#')){
    return '='
  }
  else if(thetal=='('||thetal=='#'||thetal2=='('||(thetal=='+'||thetal=='-')&&(thetal2=='×'||thetal2=='÷'||thetal2=='%')){
    return '<'
  }
  else return '>'
}


//计算两数的运算结果
function Operate(first:string,theta:string,second:string):string{
  switch (theta){
    case '+':
      return ((+first)+(+second)).toString()
    case '-':
      return ((+first)-(+second)).toString()
    case '×':
      return ((+first)*(+second)).toString()
    case '÷':
      return ((+first)/(+second)).toString()
    case '%':
      return ((+first)%(+second)).toString()
  }
  return 'NaN'
}

2、在CharacterStack.ets中进行字符栈的初始化,并进行导出;

//运算符栈
 class CharacterStack{
  private characters:string[]

  constructor() {
    this.characters=[]
  }
  //入栈
  push(item:string){
    this.characters.push(item)
  }
  //出栈
  pop(){
    return this.characters.pop()
  }
  //返回栈顶元素
  peek(){
    return this.characters[this.characters.length-1]
  }
  //判断栈是否为空
  isEmpty(){
    return this.characters.length===0
  }
  //清空栈内元素
  clear(){
    this.characters=[]
  }
  //获取栈内元素数量
  size():number{
    return this.characters.length
  }
}
const characterstack =new CharacterStack()
export default  characterstack

3、在NumberStack.ets中进行数字栈的初始化,并进行导出;

//数字栈
export class NumberStack{
  private numbers:string[]

  constructor() {
    this.numbers=[]
  }
  //入栈
  push(item:string){
    this.numbers.push(item)
  }
  //出栈
  pop(){
    return this.numbers.pop()
  }
  //返回栈顶元素
  peek(){
    return this.numbers[this.numbers.length-1]
  }
  //判断栈是否为空
  isEmpty(){
    return this.numbers.length===0
  }
  //清空栈内元素
  clear(){
    this.numbers=[]
  }
  //获取栈内元素数量
  size():number{
    return this.numbers.length
  }
}
const numberstack =new NumberStack()
export default  numberstack

已解决问题
1、括号不对应,对前括号去掉,或直接报错;
2、重复输入符号的问题;
3、首相为符号的问题;

存在问题
1、“3-”不可以输出正常的3;
在这里插入图片描述
2、对负数不可进行计算;…(存在未发现问题)

3、完整代码(一个文件)

@Entry
@Component
struct calcultorDemo {
  @State value:string =''
  @State result:string=''
  @State opacityValue:number=0
  @State fontSizeValue:number=30
  numbers :string[]=['(',')','÷','×','1','2','3','-','4','5','6','+','7','8','9']
  numbers2:string[]=['%','0','.']
  build() {
    Column(){
      Column(){
        TextInput({text:this.value})
          .height(80)
          .margin({top:60})
          .placeholderFont({size:60})
          .fontSize(60)
          .fontWeight(450)
          .textAlign(TextAlign.End)
          .backgroundColor(Color.White)
        Text(this.result)
          .opacity(this.opacityValue)
          .width('100%')
          .height(60)
          .fontSize(this.fontSizeValue)
          .fontWeight(450)
          .textAlign(TextAlign.End)
          .animation({duration:500})
            // @ts-ignore
          .textOverflow(TextOverflow.Clip)
      }
      Column(){//计算机主体页面
        Grid(){
          GridItem(){
            Text('MC')
              .TextStyle()
          }
          .oneStyle()//MC(清零)工具

          GridItem(){
            Text('MR')
              .TextStyle()
          }
          .oneStyle()

          GridItem(){
            Image($r('app.media.delete'))
              .fillColor(Color.Blue)
              .height(40)
          }
          .oneStyle()
          .onClick(()=>{
            this.value=this.value.slice(0,this.value.length-1)
            this.result=''
          })

          GridItem(){
            Text('C')
              .TextStyle()
          }
          .oneStyle()
          .onClick(()=>{
            this.value=''
            this.result=''
          })

          ForEach(this.numbers,item=>{
            GridItem(){
              Text(item)
                .TextStyle()
            }
            .onClick(()=>{
              if (this.value === '' && (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%')) {
                return
              }//判断点击的第一个字符是不是运算符,若是则返回
              if (this.value[this.value.length - 1] === '+' || this.value[this.value.length - 1] === '-' ||
              this.value[this.value.length - 1] === '×' || this.value[this.value.length - 1] === '÷' ||
              this.value[this.value.length - 1] === '%') {
                // 如果当前点击的是运算符,则替换最后一个运算符
                if (item === '+' || item === '-' || item === '×' || item === '÷' || item === '%') {
                  this.value = this.value.slice(0, this.value.length - 1) + item
                } else {
                  this.value += item
                }
              } else {
                this.value = this.value.concat(item)
              }
            })
            .oneStyle()
          })

          GridItem(){
            Text('=')
              .TextStyle()
              .fontColor(Color.White)
          }
          .rowStart(5)
          .rowEnd(6)
          .borderRadius(40)
          .backgroundColor(Color.Blue)
          .onClick(()=>{
            this.result=total(checkParentheses(this.value+'#').cleanedExpression)
            this.opacityValue=1
            this.fontSizeValue=50
          })

          ForEach(this.numbers2,item=>{
            GridItem(){
              Text(item)
                .TextStyle()
            }
            .onClick(()=>{this.value=this.value.concat(item)})
            .oneStyle()
          })
        }
        .width('100%')
        .height(500)
        .columnsTemplate('1fr 1fr 1fr 1fr')
        .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
        .columnsGap(8)
        .rowsGap(12)
        .padding(12)
        .backgroundColor('#fff6f0f0')
        .borderRadius({topLeft:20,topRight:20})
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.End)
    }
    .height('100%')
  }
}
@Styles function oneStyle(){
  .backgroundColor(Color.White)
  .height(70)
  .width(70)
  .borderRadius(40)
  .shadow({color:Color.Gray,radius:5})
}
@Extend(Text) function TextStyle() {
  .fontSize(25)
  .fontWeight(400)
}


//运算符栈
class CharacterStack{
  private characters:string[]

  constructor() {
    this.characters=[]
  }
  //入栈
  push(item:string){
    this.characters.push(item)
  }
  //出栈
  pop(){
    return this.characters.pop()
  }
  //返回栈顶元素
  peek(){
    return this.characters[this.characters.length-1]
  }
  //判断栈是否为空
  isEmpty(){
    return this.characters.length===0
  }
  //清空栈内元素
  clear(){
    this.characters=[]
  }
  //获取栈内元素数量
  size():number{
    return this.characters.length
  }
}
const characterstack =new CharacterStack()


//数字栈
export class NumberStack{
  private numbers:string[]

  constructor() {
    this.numbers=[]
  }
  //入栈
  push(item:string){
    this.numbers.push(item)
  }
  //出栈
  pop(){
    return this.numbers.pop()
  }
  //返回栈顶元素
  peek(){
    return this.numbers[this.numbers.length-1]
  }
  //判断栈是否为空
  isEmpty(){
    return this.numbers.length===0
  }
  //清空栈内元素
  clear(){
    this.numbers=[]
  }
  //获取栈内元素数量
  size():number{
    return this.numbers.length
  }
}
const numberstack =new NumberStack()


//检查括号是否多余,且若前括号多余则去掉多余的前括号
function checkParentheses(expression: string): { valid: boolean, cleanedExpression: string } {
  let stack = []
  let cleanedExpression = expression

  for (let ch of expression) {
    if (ch === '(') {
      stack.push(ch)
    } else if (ch === ')') {
      if (stack.length === 0) {
        return { valid: false, cleanedExpression: expression }
      }
      stack.pop()
    }
  }
  // 去掉多余的前括号
  while (stack.length > 0) {
    let index = cleanedExpression.indexOf('(')
    if (index !== -1) {
      cleanedExpression = cleanedExpression.slice(0, index) + cleanedExpression.slice(index + 1)
    }
    stack.pop()
  }
  console.log(cleanedExpression)
  return { valid: stack.length === 0, cleanedExpression }

}


function total(expression:string){
  characterstack.push('#')
  let i=0
  let ch=expression[i]
  while (ch!='#'||characterstack.peek()!='#') {
    if(!Instring(ch)){//进行多位数的入栈
      let numStr = ch
      while (i + 1 < expression.length && !Instring(expression[i + 1])) {
        ch = expression[++i]
        numStr += ch
      }
      numberstack.push(numStr)
      ch = expression[++i]
    }
    else{

      switch (Precede(characterstack.peek(),ch)){
        case '<':{
          characterstack.push(ch);
          ch=expression[++i]
          break
        }
        case '>':{
          let theta= characterstack.pop()
          let b=numberstack.pop()
          let a=numberstack.pop()
          numberstack.push(Operate(a,theta,b))
          break
        }
        case '=':{
          characterstack.pop()
          ch=expression[++i]
          break
        }
      }
    }
  }
  return numberstack.peek()
}


//判断ch是否为运算符
function Instring(ch:string){
  let num:string[]=['+','-','(',')','÷','×','#','%']
  return num.includes(ch)
}


//判断运算符的优先级
function Precede(thetal:string,thetal2:string):string{
  if((thetal=='('&&thetal2==')')||(thetal=='#'&&thetal2=='#')){
    return '='
  }
  else if(thetal=='('||thetal=='#'||thetal2=='('||(thetal=='+'||thetal=='-')&&(thetal2=='×'||thetal2=='÷'||thetal2=='%')){
    return '<'
  }
  else return '>'
}


//计算两数的运算结果
function Operate(first:string,theta:string,second:string):string{
  switch (theta){
    case '+':
      return ((+first)+(+second)).toString()
    case '-':
      return ((+first)-(+second)).toString()
    case '×':
      return ((+first)*(+second)).toString()
    case '÷':
      return ((+first)/(+second)).toString()
    case '%':
      return ((+first)%(+second)).toString()
  }
  return 'NaN'
}
转载请注明出处或者链接地址:https://www.qianduange.cn//article/16360.html
标签
harmonyos
评论
发布的文章

CSS3 动画

2024-04-17 21:04:24

JWT(JSON Web Token)

2024-08-30 03:08:56

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!