java-swing编程,实现计算器——支持四则运算

==== 2018年4月19日 17:56:20 更新 ===

项目地址在:https://github.com/qiao1406/java_calcultor/tree/dev



这两个星期学习了swing模块的一些内容,学习了java的GUI编程,自己动手写了一个计算器

首先是整个计算器的图形框架类CalFrame类


package Calculator;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;


public class CalFrame extends JFrame {
	
	//配色
	private final static Color OP_COLOR = new Color(251, 150, 110);
	private final static Color NUM_COLOR = new Color(36, 147, 190);
	private final static Color EQUAL_COLOR = new Color(239, 187, 36);
	private final static Color CLR_COLOR = new Color(50, 252, 75);
	private final static Color DEL_COLOR = new Color(0, 152, 120);
	
	private final static Font FONT1 = new Font("黑体", Font.BOLD, 20);
	private final static Font FONT2 = new Font("微软雅黑", Font.PLAIN, 20);
	private final static Font FONT3 = new Font("微软雅黑", Font.PLAIN, 15);
	
	private JButton num0 = new JButton("0");
	private JButton num1 = new JButton("1");
	private JButton num2 = new JButton("2");
	private JButton num3 = new JButton("3");
	private JButton num4 = new JButton("4");
	private JButton num5 = new JButton("5");
	private JButton num6 = new JButton("6");
	private JButton num7 = new JButton("7");
	private JButton num8 = new JButton("8");
	private JButton num9 = new JButton("9");
	private JButton decimalPoint = new JButton(".");
	private JButton addButton = new JButton("+");
	private JButton minusButton = new JButton("-");
	private JButton mulButton = new JButton("X");
	private JButton divButton = new JButton("÷");
	private JButton equalButton = new JButton("=");
	private JButton leftBracket = new JButton("(");
	private JButton rightBracket = new JButton(")");
	private JButton clearButton = new JButton("Clear");
	private JButton deleteButton = new JButton("Del");
	
	private JLabel equationLabel = new JLabel("算式");
	private JLabel resultLabel = new JLabel("结果");
	private JTextArea equation = new JTextArea(2,30);
	private JTextArea result = new JTextArea(1,30);

	private JPanel jp1 = new JPanel();
	private JPanel jp2 = new JPanel();

	public CalFrame () {

		equation.setEditable(false);
		result.setEditable(false);
		
		//颜色,字体设置
		colorAndFontSettings();
		
		//添加动作
		actionSettings();
		
		//设置各个组成部分的位置
		positionSettings();
		
		//其他设置
		setLayout( new GridLayout(2,1));
		add(jp1);
		add(jp2);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);
		setTitle("QiaoCalculator v2.3");
		setSize(500, 400);
		setLocation(200, 200);
		setVisible(true);
		
	}
	
	public static void main ( String[] args ) {
		new CalFrame();
	}
	
	private void colorAndFontSettings () {
		
		equationLabel.setFont(FONT2);
		resultLabel.setFont(FONT2);
		equation.setFont(FONT3);
		result.setFont(FONT3);
		
		num0.setBackground(NUM_COLOR);
		num1.setBackground(NUM_COLOR);
		num2.setBackground(NUM_COLOR);
		num3.setBackground(NUM_COLOR);
		num4.setBackground(NUM_COLOR);
		num5.setBackground(NUM_COLOR);
		num6.setBackground(NUM_COLOR);
		num7.setBackground(NUM_COLOR);
		num8.setBackground(NUM_COLOR);
		num9.setBackground(NUM_COLOR);
		decimalPoint.setBackground(NUM_COLOR);
		
		addButton.setBackground(OP_COLOR);
		addButton.setFont(FONT1);
		minusButton.setBackground(OP_COLOR);
		minusButton.setFont(FONT1);
		mulButton.setBackground(OP_COLOR);
		mulButton.setFont(FONT1);
		divButton.setBackground(OP_COLOR);
		divButton.setFont(FONT1);
		leftBracket.setBackground(OP_COLOR);
		leftBracket.setFont(FONT1);
		rightBracket.setBackground(OP_COLOR);
		rightBracket.setFont(FONT1);
		equalButton.setBackground(EQUAL_COLOR);
		equalButton.setFont(FONT1);
		clearButton.setBackground(CLR_COLOR);
		deleteButton.setBackground(DEL_COLOR);
	}
	
	private void actionSettings () {
		
		num0.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "0");
			}
		});
		num1.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "1");
			}
		});
		num2.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "2");
			}
		});
		num3.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "3");
			}
		});
		num4.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "4");
			}
		});
		num5.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "5");
			}
		});
		num6.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "6");
			}
		});
		num7.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "7");
			}
		});
		num8.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "8");
			}
		});
		num9.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "9");
			}
		});
		decimalPoint.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + ".");
			}
		});
		addButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "+");
			}
		});
		minusButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "-");
			}
		});
		mulButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "X");
			}
		});
		divButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "÷");
			}
		});
		leftBracket.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + "(");
			}
		});
		rightBracket.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText(equation.getText() + ")");
			}
		});
		equalButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				
				String cmd = equation.getText();
				Calculate cl = new Calculate();
				
				String resultMsg = cl.calResult(cmd);
				if ( resultMsg.equals("算式格式错误") || resultMsg.equals("除数不能为0") ) {
					JOptionPane.showMessageDialog(null, resultMsg, "错误", JOptionPane.WARNING_MESSAGE);
				}
				else {
					result.setText(resultMsg);
				}

			}
		});
		clearButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				equation.setText("");
				result.setText("");
			}
		});
		deleteButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if( !( equation.getText().equals("")) ) {
					StringBuffer sb = new StringBuffer();
					sb.append(equation.getText());
					sb.delete(sb.length()-1 , sb.length());
					equation.setText(sb.toString());
				}
			}
		});
		
	}
	
	private void positionSettings () {
		
		jp1.add(equationLabel);
		jp1.add(equation);
		jp1.add(resultLabel);
		jp1.add(result);
		equationLabel.setBounds(0, 0, 50, 30);
		equationLabel.setLocation(0, 0);
		equation.setBounds(50, 0, 150, 30);
		equation.setLocation(50, 0);
		resultLabel.setBounds(0, 30, 50, 30);
		resultLabel.setLocation(0, 30);
		result.setBounds(50, 30, 150, 30);
		result.setLocation(50, 30);

		jp2.setLayout(new GridLayout(4, 5));
		// line-1
		jp2.add(num7);
		jp2.add(num8);
		jp2.add(num9);
		jp2.add(addButton);
		jp2.add(leftBracket);
		// line-2
		jp2.add(num4);
		jp2.add(num5);
		jp2.add(num6);
		jp2.add(minusButton);
		jp2.add(rightBracket);
		// line-3
		jp2.add(num1);
		jp2.add(num2);
		jp2.add(num3);
		jp2.add(mulButton);
		jp2.add(clearButton);
		// line-4
		jp2.add(num0);
		jp2.add(decimalPoint);
		jp2.add(equalButton);
		jp2.add(divButton);
		jp2.add(deleteButton);
		
		jp1.setLocation(0, 0);
		jp1.setVisible(true);
		jp2.setLocation(0, 100);
		jp2.setVisible(true);
		
	}
	
}

然后就是处理算式的类Calculate类,在处理算式的时候利用了某网友提供的算法,之后打算自己用后缀表达式和二叉树结构来重构这部分代码


package Calculator;

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import sun.security.krb5.internal.ccache.CCacheInputStream;

public class Calculate {
	
	private Stack numStack = new Stack();
	private Stack sybStack = new Stack(); 
	
	public String calResult ( String equation ) {
		
		//替换乘除号
		equation = equation.replace("X", "*");
		equation = equation.replace("÷", "/");
		
		//处理负号
		equation = negativeNumTransfer(equation);
		
		if ( !checkFormat(equation) ) {
			return "算式格式错误";
		}
		
		equation += "#";
		StringBuffer tempNum = new StringBuffer();
		StringBuffer exp = new StringBuffer().append(equation);
		
		while ( exp.length() != 0 ) {
			
			String temp = exp.substring(0,1);
			exp.delete(0, 1);
			
			if( isNum(temp) )  { // temp是数字
				tempNum.append(temp);
			}
			else { // temp不是数字
				
				if (!"".equals(tempNum.toString())) {
					// 当表达式的第一个符号为括号
					double num = Double.parseDouble(tempNum.toString());
					numStack.push(num);
					tempNum.delete(0, tempNum.length());
				}
				// 用当前取得的运算符与栈顶运算符比较优先级:若高于,则因为会先运算,放入栈顶;若等于,因为出现在后面,
				// 所以会后计算,所以栈顶元素出栈,取出操作数运算;若小于,则同理,取出栈顶元素运算,将结果入操作数栈。

				// 判断当前运算符与栈顶元素优先级,取出元素,进行计算(因为优先级可能小于栈顶元素,还小于第二个元素等等,需要用循环判断)
				while ( !compare(temp.charAt(0)) && (!sybStack.empty()) ) {
					double a = numStack.pop();
					double b = numStack.pop();
					char ope = sybStack.pop();
					
					// 进行简单的计算
					if( simpleCal(ope, a, b) == false ) {
						return "除数不能为0";
					}
					
				}
				
				// 判断当前运算符与栈顶元素优先级, 如果高,或者低于平,计算完后,将当前操作符号,放入操作符栈
				refreshSybStack(temp);
				
			}
			
		}
		
		return getResultStr(numStack.pop());
	}
	
	private void refreshSybStack ( String temp) {
		if (temp.charAt(0) != '#') {
			sybStack.push(new Character(temp.charAt(0)));
			if (temp.charAt(0) == ')') {// 当栈顶为'(',而当前元素为')'时,则是括号内以算完,去掉括号
				sybStack.pop();
				sybStack.pop();
			}
		}
	} 
	
	private boolean simpleCal ( char ope, double a, double b ) {
		
		double result = 0;
		
		switch (ope) {
		case '+':
			result = b + a;
			numStack.push(result);
			break;
		case '-':
			result = b - a;
			numStack.push(result);
			break;
		case '*':
			result = b * a;
			numStack.push(result);
			break;
		case '/':
			
			if ( a == 0.0 ) {
				return false;
			}
			else {
				result = b / a;
				numStack.push(result);
				break;
			}
			
		}
		
		return true;
	}
	
	private String negativeNumTransfer( String equation ) {
		// 处理算式,将表示负数的部分进行改动,转成calResult方法支持的 
		
		if( equation.length() <= 1 ) {
			return equation;
		}
		
		StringBuffer str = new StringBuffer().append(equation);
		
		for ( int i = 0; i < str.length()-1; ++i ) {
			
			if( !str.substring(i, i+1).equals("-") ) {
				continue;
			}
			
			if ( i == 0 ) {
				char temp = str.charAt(1);
				if( isNumChar(temp) || isDecimalPoint(temp) || isLeftBracket(temp) ) {
					str.insert(0, "0");
					i++;
				}
			}
			else {
				char last = str.charAt(i-1);
				char next = str.charAt(i+1);
				
				if( isLeftBracket(last) &&
					( isNumChar(next) || isDecimalPoint(next) || isLeftBracket(next) ) ) {
					str.insert(i, "0");
					i++;
				}
			}
		}
				
		
		return str.toString();
	}
	
	
	
	private boolean checkFormat ( String equation ) {
		char[] c = equation.toCharArray();
		int singleBracket = 0;
		
		for( int i = 0; i < c.length; ++i ) {
			
			if( isLeftBracket(c[i]) ) {
				singleBracket++;
			}
			if ( isRightBracket(c[i]) ) {
				singleBracket--;
			}
			
			if ( i == 0 ) { //第1个元素只能是[0-9]或者是左括号
				if( !isLeftBracket(c[i]) && !isNumChar(c[i]) ) {
					return false;
				}
			}
			else if ( isNumChar(c[i]) || isDecimalPoint(c[i]) ) { //数字左边不能是右括号
				if ( isRightBracket(c[i-1]) ) {
					return false;
				}
			}
			else if( isLeftBracket(c[i]) )  { // 左括号的左边不能是数字和右括号
				if ( isNumChar(c[i-1]) || isDecimalPoint(c[i-1]) || isRightBracket(c[i-1]) ) {
					return false;
				}
			}
			else {  // 右括号和四则运算符的左边只能是数字或者右括号
				if ( !isNumChar(c[i-1]) && !isRightBracket(c[i-1]) ) {
					return false;
				}
			}
			
		}
		
		return singleBracket == 0;
	}

	private static boolean isNum ( String temp ) {
		return temp.matches("[0-9]") || temp.equals(".");
	}
	
	private static boolean isLeftBracket ( char c ) {
		return c == '(';
	}
	
	private static boolean isRightBracket ( char c ) {
		return c == ')';
	}
	
	private static boolean isDecimalPoint ( char c ) {
		return c == '.';
	}
	
	private static boolean isNumChar ( char c ) {
		return ( c >= '0' && c <= '9' );
	}

	private boolean compare (char str) {
		if ( sybStack.empty() ) {
			// 当为空时,显然 当前优先级最低,返回高
			return true;
		}
		char last = (char) sybStack.lastElement();
		// 如果栈顶为'('显然,优先级最低,')'不可能为栈顶。
		if (last == '(') {
			return true;
		}
		switch (str) {
		case '#':
			return false;// 结束符
		case '(':
			// '('优先级最高,显然返回true
			return true;
		case ')':
			// ')'优先级最低,
			return false;
		case '*': {
			// '*/'优先级只比'+-'高
			if (last == '+' || last == '-')
				return true;
			else
				return false;
		}
		case '/': {
			if (last == '+' || last == '-')
				return true;
			else
				return false;
		}
		// '+-'为最低,一直返回false
		case '+':
			return false;
		case '-':
			return false;
		}
		return true;
	}
	
	private String getResultStr ( double result ) {
		StringBuffer s = new StringBuffer().append( result + "" );
		
		if ( s.substring(s.length() - 2).equals(".0") ) {
			s.delete( s.length()-2 , s.length() );
		}
		
		return s.toString();
	}
	
}


附:版本信息日志

版本v1.0[2016年8月16日 07:01:32]
1.实现了两个数之间的四则运算
2.添加了对除法中除数为0的处理


版本v1.1[2016年8月17日 05:01:18]
优化界面
给按钮添加了颜色


版本v1.2[2016年8月17日 05:02:05]
进一步优化界面
增加异常处理
添加v1分支


版本v2.0[2016年8月25日 06:05:21]
增加了对四则运算的支持
增加了括号和小数点,数据类型改成double
优化了界面,式子和结果分行显示


版本v2.1[2016年8月25日 19:35:08]
增加了对式子格式的检查方法checkFormat
增加了对double类型除法0的判断
优化代码结构,将Calculate类的calResult方法模块化


版本v2.2[2016年8月26日 23:28:49]
增加了对负数的支持
改变了字体,使得界面更加友好


版本v2.3[2016年8月27日 19:25:19]
将异常结果用弹窗的形式来呈现
优化代码结构,将CalFrame类的构造方法的代码进行模块化 

你可能感兴趣的:(Java学习)