1.界面设计

使用的ConstraintLayout作为主布局容器。
关于ConstrainLayout如何使用
结果如下

设计代码如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/output"android:layout_width="match_parent"android:layout_height="180dp"android:gravity="bottom|right"style="@style/OutputTextStyle"android:textStyle="bold"android:hint="0"android:padding="10dp"app:layout_constraintBottom_toTopOf="@id/line"app:layout_constraintLeft_toLeftOf="parent"/><Viewandroid:id="@+id/line"android:layout_width="match_parent"android:layout_height="3dp"app:layout_constraintBottom_toTopOf="@id/add"android:background="@color/black"/><Buttonandroid:id="@+id/one"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:text="1"android:layout_marginLeft="5dp"app:layout_constraintBottom_toTopOf="@id/dot"app:layout_constraintRight_toLeftOf="@id/two"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/four"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="1.0"android:onClick="onClick" /><Buttonandroid:id="@+id/two"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:text="2"app:layout_constraintBottom_toTopOf="@id/zero"app:layout_constraintLeft_toRightOf="@+id/one"android:onClick="onClick"/><Buttonandroid:id="@+id/three"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="3"app:layout_constraintBottom_toTopOf="@id/equal"app:layout_constraintLeft_toRightOf="@id/two"android:onClick="onClick"/><Buttonandroid:id="@+id/del"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="C"app:layout_constraintBottom_toTopOf="@id/nine"app:layout_constraintLeft_toRightOf="@id/rightParenthesis"android:onClick="onClick"/><Buttonandroid:id="@+id/four"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:layout_marginLeft="5dp"android:text="4"app:layout_constraintBottom_toTopOf="@id/one"app:layout_constraintStart_toStartOf="parent"android:onClick="onClick"/><Buttonandroid:id="@+id/five"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:text="5"app:layout_constraintStart_toEndOf="@id/four"app:layout_constraintBottom_toTopOf="@id/two"android:onClick="onClick"/><Buttonandroid:id="@+id/six"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="6"app:layout_constraintStart_toEndOf="@id/five"app:layout_constraintBottom_toTopOf="@id/three"android:onClick="onClick"/><Buttonandroid:id="@+id/seven"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:layout_marginLeft="5dp"android:text="7"app:layout_constraintBottom_toTopOf="@id/four"app:layout_constraintLeft_toLeftOf="parent"android:onClick="onClick"/><Buttonandroid:id="@+id/eight"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:text="8"app:layout_constraintBottom_toTopOf="@id/five"app:layout_constraintLeft_toRightOf="@id/seven"android:onClick="onClick"/><Buttonandroid:id="@+id/nine"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="9"app:layout_constraintBottom_toTopOf="@id/six"app:layout_constraintLeft_toRightOf="@id/eight"android:onClick="onClick"/><Buttonandroid:id="@+id/dot"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:layout_marginLeft="5dp"android:text="."app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"android:onClick="onClick"/><Buttonandroid:id="@+id/leftParenthesis"style="@style/ButtonStyle"android:layout_width="100dp"android:layout_height="80dp"android:layout_marginLeft="5dp"android:text="("app:layout_constraintBottom_toTopOf="@id/seven"app:layout_constraintStart_toStartOf="parent"android:onClick="onClick"/><Buttonandroid:id="@+id/rightParenthesis"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text=")"app:layout_constraintLeft_toRightOf="@id/leftParenthesis"app:layout_constraintBottom_toTopOf="@id/eight"android:onClick="onClick"/><Buttonandroid:id="@+id/equal"android:layout_width="200dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="="app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toRightOf="@+id/zero"android:onClick="onClick"/><Buttonandroid:id="@+id/zero"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="0"app:layout_constraintLeft_toRightOf="@id/dot"app:layout_constraintBottom_toBottomOf="parent"android:onClick="onClick"/><Buttonandroid:id="@+id/add"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="+"app:layout_constraintLeft_toRightOf="@id/del"app:layout_constraintBottom_toTopOf="@id/sub"android:onClick="onClick"/><Buttonandroid:id="@+id/sub"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="-"app:layout_constraintBottom_toTopOf="@id/mul"app:layout_constraintLeft_toRightOf="@id/nine"android:onClick="onClick"/><Buttonandroid:id="@+id/mul"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="*"app:layout_constraintBottom_toTopOf="@id/div"app:layout_constraintLeft_toRightOf="@id/six"android:onClick="onClick"/><Buttonandroid:id="@+id/div"android:layout_width="100dp"android:layout_height="80dp"style="@style/ButtonStyle"android:text="/"app:layout_constraintBottom_toTopOf="@id/equal"app:layout_constraintLeft_toRightOf="@id/three"android:onClick="onClick"/></androidx.constraintlayout.widget.ConstraintLayout>

使用到的values文件,colors.xml和styles.xml

2.功能需求

能正确计算加、减、乘、除、包含小括号,小数,负数

3.具体实现

用到的技术点
①中缀转后缀生成后缀表达式(计算器的核心)
中最转后缀算法:
需要两个栈:符号栈和数字栈
1.符号栈为空,当前元素直接入栈;或者当前字符为"(“,直接入栈
2.遇到“)”,将符号栈里内容一一出栈,直到遇到”("
3.其他字符的情况,比较当前字符与符号栈顶字符的优先级,若当前元素的优先级大于栈顶元素的优先级,直接入栈;否则,一直出栈,直到符合当前元素的优先级大于栈顶元素优先级的要求,或者遇到“(”
如下是代码实现,包含了后缀表达式的运算

public Result SuffixesToSuffixes(String expression) throws CalException, ArithmeticException {String resExpression = "";Stack<Character> stack = new Stack<>();Stack<BigDecimal> numberStack = new Stack<>();Result result = new Result();boolean isMinus = false; //假设不是负数for(int i = 0 ; i < expression.length();  ){String str1 = "";//判断是负数if(expression.charAt(i) == '-' && (i + 1) < expression.length() && isNumber(expression.charAt(i + 1))){if(i == 0 || isOperator(expression.charAt(i - 1))){isMinus = true;i++;continue;}}//是数字组成部分while (i < expression.length() && isNumberOrDot(expression.charAt(i))) {str1 = str1 + expression.charAt(i++);}//数字内容处理if(!str1.equals("")){resExpression += str1;//检查当前数字是否合法service.checkNumber(str1);//将String转换为对应的BigDecimal对象BigDecimal bd = new BigDecimal(str1);//转换成负数if(isMinus){BigDecimal bd1 = new BigDecimal("-1");bd = bd.multiply(bd1);isMinus = false;}//为了显式好看,处理double类型尾部是0的情况,直接转换成对应整数if(bd.doubleValue() == bd.intValue()){bd = new BigDecimal(bd.intValue());}else{bd = new BigDecimal(bd.doubleValue());}//将BigDecimal对象压栈numberStack.push(bd);//其他字符处理}else{//1.栈为空,当前元素直接入栈;或者当前字符为"(",直接入栈if(stack.isEmpty() || expression.charAt(i) == '('){stack.push(expression.charAt(i));//2.遇到“)”,将栈里内容一一出栈,直到遇到"("}else if(expression.charAt(i) == ')'){while (stack.peek() != '('){char c = stack.pop();resExpression += c;service.calculate(numberStack, c);}//“(”出栈stack.pop();}else{//3.其他字符的情况,比较当前字符与栈顶字符的优先级,若当前元素的优先级大于栈顶元素的优先级,直接入栈;否则,一直出栈,// 直到符合要求,或者遇到“(”if(operatorPrecedent(expression.charAt(i)) > operatorPrecedent(stack.peek())){stack.push(expression.charAt(i));}else{while(!stack.isEmpty() && stack.peek() != '('&& operatorPrecedent(expression.charAt(i)) <= operatorPrecedent(stack.peek())) {char c = stack.pop();resExpression += c;service.calculate(numberStack, c);}stack.push(expression.charAt(i));}}i++;}}while(!stack.isEmpty()){char c = stack.pop();resExpression += c ;service.calculate(numberStack, c);}result.setNumberStack(numberStack);result.setPostfixExpression(resExpression);return result;}

②运算符优先级比较

int operatorPrecedent(char c){if(c == '*' || c == '/')return 2;if(c == '+' || c == '-')return 1;return -1;}

③判断是数字

 boolean isNumber(char c){if(c == '0' || c == '1' || c == '2' || c=='3' || c=='4'|| c=='5' || c=='6' || c=='7' || c=='8' || c=='9'){return true;}return false;}

④判断是数字的组成部分(比数字多了一个小数点)

 static boolean isNumberOrDot(char c){if(isNumber(c) || c == '.'){return true;}return false;}

⑤判断是合法小数

  void checkNumber(String str) throws CalException {int dotCount = 0;for(int i = 0 ; i < str.length(); i++){if(str.charAt(i) == '.')dotCount++;}if(dotCount > 1){throw new CalException("表达式输入有误!");}}

⑥判断是操作数符号

 boolean isOperator(char ch){if(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch =='(')return true;return false;}

⑦负数正确转换成数字
在中缀转后缀表达式的算法中有包含:
判断是负数,则isMinus标志设为true,后期数字转换的过程中判断,如果标志为true则给转换出来的数字乘-1,转换成负数。

 //判断是负数if(expression.charAt(i) == '-' && (i + 1) < expression.length() && isNumber(expression.charAt(i + 1))){if(i == 0 || isOperator(expression.charAt(i - 1))){isMinus = true;i++;continue;}}

⑧大数运算
数字使用BigDecimal

4.可能出现的闪退情况

除0异常
表达式错误(包含乱写表达式,数字输入有问题,符号或者数字多了少了)
大数除法需要指定精度

5.处理闪退情况

自定义异常类,在处理表达式的过程中抛异常,在调用该方法的地方处理异常

6.考虑是否使用架构mvc、mvp、mvvm等…

没考虑

7.可以优化的地方

关于小数点的问题,可以在用户按下小数点,自动补即按 ‘.’ 显示“0.”,并在按’.‘的逻辑中不让用户对一个数字可以按出两个及以上的’.',这样也就不需要检查数字的合法性了。

运行结果截图

1.表达式"-1*2+(-1+2)"


2.表达式"1.1+23(4.4*5+6)*7"


代码及简要讲解

我写了四个类和一个Android主界面类来实现此功能:
①CalException:自定义的异常类
②CalService:处理计算过程和检查数字合法性
③ExpressionTool:完成中缀转后缀以及用到的一些判断方法
④Result:自定义返回的数据类型,属性包含:一个栈(用来保存数字)和一个String(用来保存中缀转后缀,生成的后缀表达式)
⑤MainActivity.java:主界面逻辑代码

其中,最主要的是ExpressionTool类,它完成了中缀转后缀的整个过程.
代码如下:
①CalException.java 这是一个自定义的异常类

public class CalException extends  Exception{static final long serialVersionUID = -3381275169931242948L;public CalException(String msg){super(msg);}
}

②CalService.java 这个类用于计算

package com.example.mycalculate;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Stack;public class CalService {//检查数字(小数)void checkNumber(String str) throws CalException {int dotCount = 0;for(int i = 0 ; i < str.length(); i++){if(str.charAt(i) == '.')dotCount++;}if(dotCount > 1){throw new CalException("表达式输入有误!");}}//计算两个数的某种运算public void  calculate(Stack<BigDecimal> numberStack , char ch){BigDecimal num2 = numberStack.pop();BigDecimal num1 = numberStack.pop();BigDecimal res = calculate(num1, num2, ch);numberStack.push(res);}//具体计算public BigDecimal calculate(BigDecimal num1, BigDecimal num2, char c) throws ArithmeticException{BigDecimal res = new BigDecimal("0");switch (c){case '+': res = num1.add(num2);break;case '-': res = num1.subtract(num2);break;case '*': res = num1.multiply(num2);break;case '/':if(num2.equals(0))throw new ArithmeticException();res = num1.divide(num2,16 ,RoundingMode.HALF_UP);//注意相除产生无线小数的情况,需要指明精度,以及舍入模式break;}return res;}
}

③ExpressionTool.java
主要讲一下方法SuffixesToSuffixes(String expression),这个方法中完成了中缀转后缀并计算,传入一个String类型的表达式。
首先需要两个栈,数字栈numberStack,运算符号栈stack.
表达式处理逻辑为:

package com.example.mycalculate;
import java.math.BigDecimal;
import java.util.Stack;public class ExpressionTool {CalService service = new CalService();/*** 中缀转后缀表达式* @param expression* @return*/public Result SuffixesToSuffixes(String expression) throws CalException, ArithmeticException {String resExpression = "";Stack<Character> stack = new Stack<>();Stack<BigDecimal> numberStack = new Stack<>();Result result = new Result();boolean isMinus = false; //假设不是负数for(int i = 0 ; i < expression.length();  ){String str1 = "";//判断是负数if(expression.charAt(i) == '-' && (i + 1) < expression.length() && isNumber(expression.charAt(i + 1))){if(i == 0 || isOperator(expression.charAt(i - 1))){isMinus = true;i++;continue;}}//是数字组成部分while (i < expression.length() && isNumberOrDot(expression.charAt(i))) {str1 = str1 + expression.charAt(i++);}//数字内容处理if(!str1.equals("")){resExpression += str1;//检查当前数字是否合法service.checkNumber(str1);//将String转换为对应的BigDecimal对象BigDecimal bd = new BigDecimal(str1);//转换成负数if(isMinus){BigDecimal bd1 = new BigDecimal("-1");bd = bd.multiply(bd1);isMinus = false;}//为了显式好看,处理double类型尾部是0的情况,直接转换成对应整数if(bd.doubleValue() == bd.intValue()){bd = new BigDecimal(bd.intValue());}else{bd = new BigDecimal(bd.doubleValue());}//将BigDecimal对象压栈numberStack.push(bd);//其他字符处理}else{//1.栈为空,当前元素直接入栈;或者当前字符为"(",直接入栈if(stack.isEmpty() || expression.charAt(i) == '('){stack.push(expression.charAt(i));//2.遇到“)”,将栈里内容一一出栈,直到遇到"("}else if(expression.charAt(i) == ')'){while (stack.peek() != '('){char c = stack.pop();resExpression += c;service.calculate(numberStack, c);}//“(”出栈stack.pop();}else{//3.其他字符的情况,比较当前字符与栈顶字符的优先级,若当前元素的优先级大于栈顶元素的优先级,直接入栈;否则,一直出栈,// 直到符合要求,或者遇到“(”if(operatorPrecedent(expression.charAt(i)) > operatorPrecedent(stack.peek())){stack.push(expression.charAt(i));}else{while(!stack.isEmpty() && stack.peek() != '('&& operatorPrecedent(expression.charAt(i)) <= operatorPrecedent(stack.peek())) {char c = stack.pop();resExpression += c;service.calculate(numberStack, c);}stack.push(expression.charAt(i));}}i++;}}while(!stack.isEmpty()){char c = stack.pop();resExpression += c ;service.calculate(numberStack, c);}result.setNumberStack(numberStack);result.setPostfixExpression(resExpression);return result;}//判断是否是操作符static boolean isOperator(char ch){if(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch =='(')return true;return false;}//判断是否是数字static boolean isNumber(char c){if(c == '0' || c == '1' || c == '2' || c=='3' || c=='4'|| c=='5' || c=='6' || c=='7' || c=='8' || c=='9'){return true;}return false;}/*** 返回操作符的优先级,“*”和“/”的优先级为2,“+”和“-”的优先级为“1”* @param c* @return*/int operatorPrecedent(char c){if(c == '*' || c == '/')return 2;if(c == '+' || c == '-')return 1;return -1;}//是数字或者'.'static boolean isNumberOrDot(char c){if(isNumber(c) || c == '.'){return true;}return false;}
}

④Result.java 自定义的返回类型,返回一个数字栈用于存中缀转后缀中的数字和一个表达式String用于返回中缀转后缀的最终后缀表达式

import java.math.BigDecimal;
import java.util.Stack;public class Result {Stack<BigDecimal> numberStack = null;String postfixExpression = "";public Result(){numberStack = new Stack<>();}public void setPostfixExpression(String postfixExpression) {this.postfixExpression = postfixExpression;}public String getPostfixExpression() {return postfixExpression;}public Stack<BigDecimal> getNumberStack() {return numberStack;}public void setNumberStack(Stack<BigDecimal> numberStack) {this.numberStack = numberStack;}
}

Android版计算器(java实现,包含小数、负数、括号)代码和讲解相关推荐

  1. Java面试题大全(Android版)

    疯狂Java面试题大全(Android版) Java核心技术部分 Java核心技术部分的面试题,可能覆盖Java基本语法.面向对象(包括类定义.方法.构造器.递归.继承.抽象类.接口.枚举以及fina ...

  2. 疯狂Java面试题大全(Android版)

    疯狂Java面试题大全(Android版) 本大全每个月会定期更新,索取网址:http://www.fkjava.org Java核心技术部分 Java核心技术部分的面试题,可能覆盖Java基本语法. ...

  3. 疯狂Java和Android面试题大全(Android版)

    疯狂Java和Android面试题大全(Android版) 本大全每个月会定期更新,索取网址:http://www.fkjava.org Java核心技术部分 Java核心技术部分的面试题,可能覆盖J ...

  4. android 开源计算器,开源Windows 10计算器应用迎来Web/Android/iOS移植版

    开源的跨平台特性,使得基于 UWP 代码的 Windows 10 计算器应用可以在非 Windows 平台上运行. Uno 在一篇博客文章中表示:Windows Calculator 完全使用标准的 ...

  5. Java游戏开发框架LGame-0 2 8版发布(含JavaSE及Android版,已有文档)

    LGame是LoonFramework框架的一部分,也可简称做"LF"或"Loon". LGame框架的创立初衷在于,构建一个高效且完善的Java游戏开发体系, ...

  6. java 比例计算器_java版计算器

    [java]代码库import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing ...

  7. Java GUI图形编程 使用awt和swing 制作简易计算器的工具包含源码和讲解 / java练习项目

    系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录 系列文章目录 前言 一.常规配置 1. 设置窗口大小 2. 获取当前屏幕的尺寸 3. 窗口居中 4. 设置窗口名称 5. 添加监听器,关闭窗口 ...

  8. JAVA桌面版、J2ME版、ANDROID版贪吃蛇

    呃,主要是想复习下J2ME,不然忘记了,就做了3个版本. 先说说本程序实现的结构: J2SE: Food类: 实现食物随机产生及判断是否会碰撞在墙壁上或蛇身上 GameWindow类:主要实现整个游戏 ...

  9. Android Studio计算器解决三角函数,反三角函数等

    文章目录 前言 一.XML布局 二.MainActivity 总结 1. 2. 3. 前言 需要设计一款通用计算器,包含基本的四则运算,还引入了括号,还要解决一些特殊的三角函数,反三角函数等,浏览了C ...

最新文章

  1. numpy数组切片:一维/二维/数组
  2. 9102年webpack4搭建vue项目
  3. C++使用数组的链表实现(附完整源码)
  4. java 性能 排序_Java常用排序算法及性能测试集合
  5. 和我一起学WCF(6):宿主(Hosting)
  6. 004-docker常用命令[二]-容器操作ps,top,attach,export
  7. Docker初级选手(一)
  8. 许可证编译器 (Lc.exe)
  9. opencv3 学习三 - 图像输入输出显示等
  10. 第四章 consul cluster
  11. Kafka 面试题(2022)
  12. 但打不开 固态硬盘 插电脑上 能识别_移动硬盘显示盘符但打不开解决教程
  13. 网页设计中有效的配色
  14. python3思维导图.xmind_Xmind和FreeMind思维导图格式互转
  15. sequoia 的详细安装步骤
  16. 单机启动schedule报错:Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: connect: connection ref
  17. VM安装centos时设置基础软件仓库时出错(设置镜像后依旧出错)
  18. 人工鱼群算法在颗粒粒径测量中应用
  19. Maxthon 浏览器被发现收集用户数据
  20. 外媒爆料:PS5将在2019年之后发售,是一次真正的更新换代!

热门文章

  1. 长春市计算机职业技术学校,长春职业技术学校
  2. HMAC-MD5签名的Java实现
  3. 软件界面设计原则(转)
  4. PyCharm界面字体放大和缩小
  5. 常见神经系统疾病的临床诊断及处理原则题库【2】
  6. ES6 生成器(Generator)
  7. 金融业务-美港股和A股的区别
  8. JS十六进制,CRC冗余,小程序发送蓝牙数据,十六进制GBK编码转换等
  9. 数值分析12 - Hermite埃尔米特插值法(要求高阶插值误差足够小)
  10. 使用微信小程序控制ESP8266(麦克纳姆轮)智能车