基于Java Swing/Awt的UI界面
目录
1.界面设计
界面由24个按钮和1个线型编辑器、1个历史文本框构成,仿照小米手机软件中简单计算器,整体配色为黑、白、蓝色,背景为白色,按钮为蓝色,边框以及表达符号为黑色;
2.功能描述
del:删去表达式的一个字符,退格
clr:清楚表达式内容
!:求阶乘符号
^:求指数幂
其他运算符和操作数:输入对应的字符
3.界面浏览
4.实现功能
1,加减乘除可配合括号正常计算
2,表达式出现不符合语法时,会在提示栏自动报错
3, 实现了取余、指数、阶乘、平方等功能
4,实现小数点后八位的精确计算
5,实现按钮和键盘对计算器的同时监听控制
5.效果:
6.代码实现
(1)定义运算优先级
static private int Precedence(char op) {
switch (op) {
case '+':
case '-':
return 1; // 定义加减运算的优先级为1
case '*':
case '/':
case '%':
return 2; // 定义乘除与取余运算的优先级为2
case '^':
return 3; // 定义次方运算的优先级为3
case '!':
return 4; // 定义阶乘运算的优先级为4
case '(':
case '=':
default:
return 0; // 定义在栈中的左括号和栈底字符的优先级为0
}
}
(2)中缀表达式到后缀表达式的转化
/**
* 中缀表达式转换后缀表达式
* @param str 中缀表达式字符串(要求以=结尾)
* @return 后缀表达式的字符串形式
* @throws Exception 中缀表达式存在错误
*/
static private String toSuffix(String str) throws Exception {
String d=str;
StringBuffer strBuf = new StringBuffer();// 存储后缀表达式
Stack<Character> stack = new Stack<Character>();// 字符栈,用于处理运算符
stack.push('=');// 压入栈底字符
int i = 0;// 中缀式字符串迭代器
if (str == null || str.equals("") || str.equals("="))
throw new Exception("表达式不能为空");
char ch = str.charAt(i);// 临时字符处理
if (ch == '.')
throw new Exception("表达式格式不规范(不能以“.”开头)");
if (str.charAt(str.length() - 1) != '=')
throw new Exception("表达式格式不规范(不以“=”结尾)");
if (ch == '-' || ch == '+')
strBuf.append("0 ");//正负号首位补零
while (ch != '=') {
if (ch == ' ')
ch = str.charAt(++i);// 读取下一个字符(忽略空格)
else if (ch == '(') {
stack.push('(');// 如果是左括号将其压入栈
ch = str.charAt(++i);
if (ch == '-' || ch == '+')
strBuf.append("0 ");//正负号补零
} else if (ch == ')') {//查看前面是否有左括号
while (stack.peek() != '(') {
if (stack.peek() == '=')//stack.peek是查看栈顶的元素但不移除它。pop方法会移除掉栈顶元素
throw new Exception("表达式括号不匹配,缺少“(”");
strBuf.append(stack.pop());//把栈顶元素追加到字符串strBuf后
}
stack.pop();
ch = str.charAt(++i);
} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^' || ch == '!' || ch == '%') {
char w = stack.peek();// 临时读取栈中的运算符
// 将现在的运算符和栈中的运算符进行优先级比较
while (Precedence(w) >= Precedence(ch)) {
strBuf.append(w);//把运算符w追加到字符串strBuf后
stack.pop();//弹出该运算符,进行下一个运算符优先级的比较
w = stack.peek();
}
stack.push(ch);//将运算符ch压入栈
ch = str.charAt(++i);
} else {
// 直接将数字和小数点加到后缀式中,把小数点的优先级设定成和数字一样
if (!((ch >= '0' && ch <= '9') || ch == '.'))
throw new Exception("表达式出现非法符号");
while ((ch >= '0' && ch <= '9') || ch == '.') {
strBuf.append(ch);//把数字ch追加到字符串strBuf后
ch = str.charAt(++i);
}
strBuf.append(' ');//把空格追加到字符串strBuf后
}
}
ch = stack.pop();
while (ch != '=') {//检查括号的完整性
if (ch == '(')
throw new Exception("表达式括号不匹配,缺少“)”");
else {
strBuf.append(ch);
ch = stack.pop();
}
}
strBuf.append('=');//把等于追加到字符串strBuf后
return strBuf.toString();//返回后缀表达式strBuf
}
(2)后缀表达式的计算
/**
* 计算后缀表达式
* @param str 后缀表达式的字符串形式
* @return 高精度浮点数
* 如果计算过程中出现循环小数则取至小数点后15位
* @throws Exception 计算过程出现错误
*/
static private BigDecimal runSuffix(String str) throws Exception {
System.out.println(str);//输入后缀表达式str
Stack<BigDecimal> stack = new Stack<BigDecimal>();//进行大数计算的栈
int i = 0;//设置后缀表达式迭代器
BigDecimal num1, num2;//临时大数变量
StringBuffer strBuf;//
while (str.charAt(i) != '=') {
strBuf = new StringBuffer();
if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
while ((str.charAt(i) >= '0' && str.charAt(i) <= '9') || str.charAt(i) == '.')
strBuf.append(str.charAt(i++));//形成数字
try {//Java的异常处理机制
stack.push(new BigDecimal(strBuf.toString()));
} catch (Exception e) {
throw new Exception("表达式计算数出错");
}
} else if (str.charAt(i) == ' ')
i++;//忽略空格
else if (str.charAt(i) == '+') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num1.add(num2));//加法运算
i++;
} else if (str.charAt(i) == '-') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num2.subtract(num1));//减法运算
i++;
} else if (str.charAt(i) == '*') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num1.multiply(num2));//乘法运算
i++;
} else if (str.charAt(i) == '%') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
BigInteger numT1 = num1.toBigIntegerExact();
BigInteger numT2 = num2.toBigIntegerExact();
stack.push(new BigDecimal(numT2.remainder(numT1).toString()));//取余运算
} catch (ArithmeticException e) {
throw new Exception("参与取余运算的数只能是整数,且除数不能为0");
} catch (Exception e) {
throw new Exception("表达式取余运算出现错误");
}
i++;
} else if (str.charAt(i) == '^') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
BigInteger numT = num1.toBigIntegerExact();
if (numT.compareTo(new BigInteger("0")) == numT.compareTo(new BigInteger("999999999")))
throw new ArithmeticException();
stack.push(num2.pow(numT.intValue()));//次方运算
} catch (ArithmeticException e) {
throw new Exception("幂的范围只能是0到9999之间,且只能是整数");
} catch (Exception e) {
throw new Exception("幂运算出现错误");
}
i++;
} else if (str.charAt(i) == '!') {
if (stack.size() < 1)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
try {
BigInteger numT = num1.toBigIntegerExact();
if (numT.compareTo(new BigInteger("0")) == numT.compareTo(new BigInteger("9999")))
throw new ArithmeticException();
int count = numT.intValue();
num2 = new BigDecimal("1");
while (count > 0)
num2 = num2.multiply(new BigDecimal(count--));//阶乘运算
stack.push(num2);
} catch (ArithmeticException e) {
throw new Exception("阶乘的范围只能是0到9999之间,且只能是整数");
} catch (Exception e) {
throw new Exception("阶乘运算出现错误");
}
i++;
} else if (str.charAt(i) == '/') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
stack.push(num2.divide(num1));//除法运算
} catch (ArithmeticException e) {
try {
//出现循环小数将其处理为小数点只保留8位
stack.push(num2.divide(num1, 8, RoundingMode.HALF_UP));
} catch (ArithmeticException e2) {
throw new Exception("除法运算出现错误(可能是0作了除数)");
}
}
i++;
}
}
if (stack.size() != 1)
throw new Exception("表达式缺少运算符");
return stack.pop();
}
(3)界面布局:
public Calculator() {
this.setTitle("Java简单计算器");//定义窗口标题
this.setSize(560, 300);//窗口大小
this.setResizable(false);//禁止用户调整大小
StringTokenizer strT = new StringTokenizer(str, " ");//将按钮字符集按空格分隔
Container c = this.getContentPane();//获取根容器
showResult.setBorder(new LineBorder(Color.BLACK, 1));//设置文本区域边框
showResult.setLineWrap(true);//文本区域可自动换行
showResult.setEditable(false);//文本区域不可编辑
showResult.addKeyListener(this);//文本区域增加键盘事件监听
showUser = new JScrollPane(showResult);//为文本区域加上拖动条
History.setBorder(new LineBorder(Color.BLACK, 1));//设置文本区域边框
History.setLineWrap(true);//文本区域可自动换行
History.setEditable(false);//文本区域不可编辑
History.addKeyListener(this);//文本区域增加键盘事件监听
historyUser = new JScrollPane(historyUser );//为文本区域加上拖动条
label.setBounds(30, 15, 100, 20);//设置标签位置及大小
this.add(label);// 新建“历史记录”标签
History.setLineWrap(true);//自动换行
History.setWrapStyleWord(true);
History.setSelectedTextColor(Color.blue);
westPanel.add(showUser, BorderLayout.CENTER);
westPanel.add(tips, BorderLayout.SOUTH);
westPanel.add(label, BorderLayout.NORTH);
westPanel.add(History,BorderLayout.NORTH);
eastPanel.setLayout(new GridLayout(6, 4, 2, 2));//布局
}
(4)计算过程处理(以等号=为表达式结束标志)
private void run() {
List<String> list=new ArrayList<>();
// Iterator<String> it=list.iterator();
StringBuffer strBuff= strBuf;
strBuff.append('=');//表达式规范化以=结尾
try {
//计算并且显示
long beforeTime = System.currentTimeMillis();
String strTemp;
showResult.setText(strBuff.append(strTemp = (CalculatorEngine.run(strBuff.toString())).toString()).toString());
list.add(strBuff.toString());
int length = list.size();
int i=length;
StringBuffer d=null;
while(i--!=0)
History.setText(list.get(i));//在历史记录框里显示计算表达式及结果
//History.setText(strBuff.toString());
strBuf.setLength(0);//表达式字符串清空
strBuf.append(strTemp);//新的计算保存原来的结果
//History.setText(strBuf+strTemp);//在历史记录框里显示计算表达式及结果
tips.setText("计算已完成");
} catch (Exception ex) {
if (ex.getMessage() == null || ex.getMessage().equals(""))
tips.setText("发生未知错误");
else
tips.setText(ex.getMessage());//将错误输出到提示标签上
}
}
7.对特殊按钮的处理
(1)当前按钮是‘del’:
if (e.getSource() == button[21]) {
strBuf.setLength(strBuf.length() > 0 ? strBuf.length() - 1 : 0);//删除一个字符,del
showResult.setText(strBuf.toString());//显示
(2)当前按钮是‘clr’:
if (e.getSource() == button[22]) {
strBuf.setLength(0);//清空,clr
showResult.setText(strBuf.toString());//显示
if (e.getSource() == button[23]) {
run();//点击=则计算
8.其他运算符或者数字时:
if ((e.getKeyChar() >= '0' && e.getKeyChar() <= '9') || e.getKeyChar() == '.' ||
e.getKeyChar() == '+' || e.getKeyChar() == '-' || e.getKeyChar() == '*' ||
e.getKeyChar() == '/' || e.getKeyChar() == '(' || e.getKeyChar() == ')' ||
e.getKeyChar() == '^' || e.getKeyChar() == '!' || e.getKeyChar() == '%' || e.getKeyChar() == ' ') {
//计算表达式的形成
showResult.setText(strBuf.append(e.getKeyChar()).toString());
9.主测试类:
class test {
public static void main(String[] args) {
Calculator cFrame = new Calculator();
cFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cFrame.setVisible(true);
//JOptionPane.showMessageDialog(null, "请注意正确使用java表达式计算器!\n");
}
}
10.不足之处:
1,历史记录文本框功能不完善,不能实现历史记录的回调
2,显示栏中输入错误表达式,只能在提示栏显示错误。
例如:可以输入多个小数点,此时提示栏仍然会报错,但是不能限制同时输入多个小数点