Android版计算器(java实现,包含小数、负数、括号)代码和讲解
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实现,包含小数、负数、括号)代码和讲解相关推荐
- Java面试题大全(Android版)
疯狂Java面试题大全(Android版) Java核心技术部分 Java核心技术部分的面试题,可能覆盖Java基本语法.面向对象(包括类定义.方法.构造器.递归.继承.抽象类.接口.枚举以及fina ...
- 疯狂Java面试题大全(Android版)
疯狂Java面试题大全(Android版) 本大全每个月会定期更新,索取网址:http://www.fkjava.org Java核心技术部分 Java核心技术部分的面试题,可能覆盖Java基本语法. ...
- 疯狂Java和Android面试题大全(Android版)
疯狂Java和Android面试题大全(Android版) 本大全每个月会定期更新,索取网址:http://www.fkjava.org Java核心技术部分 Java核心技术部分的面试题,可能覆盖J ...
- android 开源计算器,开源Windows 10计算器应用迎来Web/Android/iOS移植版
开源的跨平台特性,使得基于 UWP 代码的 Windows 10 计算器应用可以在非 Windows 平台上运行. Uno 在一篇博客文章中表示:Windows Calculator 完全使用标准的 ...
- Java游戏开发框架LGame-0 2 8版发布(含JavaSE及Android版,已有文档)
LGame是LoonFramework框架的一部分,也可简称做"LF"或"Loon". LGame框架的创立初衷在于,构建一个高效且完善的Java游戏开发体系, ...
- java 比例计算器_java版计算器
[java]代码库import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing ...
- Java GUI图形编程 使用awt和swing 制作简易计算器的工具包含源码和讲解 / java练习项目
系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录 系列文章目录 前言 一.常规配置 1. 设置窗口大小 2. 获取当前屏幕的尺寸 3. 窗口居中 4. 设置窗口名称 5. 添加监听器,关闭窗口 ...
- JAVA桌面版、J2ME版、ANDROID版贪吃蛇
呃,主要是想复习下J2ME,不然忘记了,就做了3个版本. 先说说本程序实现的结构: J2SE: Food类: 实现食物随机产生及判断是否会碰撞在墙壁上或蛇身上 GameWindow类:主要实现整个游戏 ...
- Android Studio计算器解决三角函数,反三角函数等
文章目录 前言 一.XML布局 二.MainActivity 总结 1. 2. 3. 前言 需要设计一款通用计算器,包含基本的四则运算,还引入了括号,还要解决一些特殊的三角函数,反三角函数等,浏览了C ...
最新文章
- numpy数组切片:一维/二维/数组
- 9102年webpack4搭建vue项目
- C++使用数组的链表实现(附完整源码)
- java 性能 排序_Java常用排序算法及性能测试集合
- 和我一起学WCF(6):宿主(Hosting)
- 004-docker常用命令[二]-容器操作ps,top,attach,export
- Docker初级选手(一)
- 许可证编译器 (Lc.exe)
- opencv3 学习三 - 图像输入输出显示等
- 第四章 consul cluster
- Kafka 面试题(2022)
- 但打不开 固态硬盘 插电脑上 能识别_移动硬盘显示盘符但打不开解决教程
- 网页设计中有效的配色
- python3思维导图.xmind_Xmind和FreeMind思维导图格式互转
- sequoia 的详细安装步骤
- 单机启动schedule报错:Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: connect: connection ref
- VM安装centos时设置基础软件仓库时出错(设置镜像后依旧出错)
- 人工鱼群算法在颗粒粒径测量中应用
- Maxthon 浏览器被发现收集用户数据
- 外媒爆料:PS5将在2019年之后发售,是一次真正的更新换代!