1. 程式人生 > >java-swing程式設計,實現計算器——支援四則運算

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<Double> numStack = new Stack<Double>();
	private Stack<Character> sybStack = new Stack<Character>(); 
	
	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類的構造方法的程式碼進行模組化