Advertisement
Watch on YouTube
Create a new project in Intellij with the following directory structure.
Our project consists of three main classes:
- CalculatorApp
- The main entry point of the application.
- CalculatorGui
- This class handles the graphical user interface (GUI) of the calculator.
- EventListeners
- This class manages the events generated by user interactions with the calculator buttons.
CalculatorApp
This is the entry point of our application. It initializes the GUI and sets up the event listeners for the buttons.
content_copy copypackage org.example; import javax.swing.*; import java.util.Arrays; public class CalculatorApp { private CalculatorGui calculatorGui; private EventListeners eventListeners; CalculatorApp(){ calculatorGui = new CalculatorGui(); calculatorGui.initCalculatorGui(); calculatorGui.setLocationRelativeTo(null); calculatorGui.setVisible(true); eventListeners = new EventListeners(calculatorGui); // attach event listeners to all buttons in buttons panel Arrays.stream(calculatorGui.getBtnsPanel().getComponents()).forEach( btn -> { if (btn instanceof JButton jButton){ jButton.addActionListener(eventListeners); } } ); } public static void main(String[] args){ CalculatorApp calculatorApp = new CalculatorApp(); } }
CalculatorGui
This class constructs the GUI for the calculator, including the display field and buttons for various operations. Components are added using a GridBagLayout, which allows flexible positioning.
content_copy copypackage org.example; import javax.swing.*; import java.awt.*; public class CalculatorGui extends JFrame { private JTextField displayField; private JPanel btnsPanel; private JButton zeroBtn; private JButton oneBtn; private JButton twoBtn; private JButton threeBtn; private JButton fourBtn; private JButton fiveBtn; private JButton sixBtn; private JButton sevenBtn; private JButton eightBtn; private JButton nineBtn; private JButton tenBtn; private JButton pointBtn; private JButton exponentBtn; private JButton plusBtn; private JButton minusBtn; private JButton multiplyBtn; private JButton divideBtn; private JButton deleteBtn; private JButton clearAllBtn; private JButton equalBtn; // constants private final Dimension guiDimension = new Dimension(350, 500); private final Dimension displayDimension = new Dimension(250, 50); private final Dimension btnPanelDimension = new Dimension(250, 250); private final Dimension btnDimension = new Dimension(55, 30); private final Insets leftBtnInsets = new Insets(7, 0, 7, 5); private final Insets midBtnInsets = new Insets(7, 5, 7, 5); private final Insets rightBtnInsets = new Insets(7, 5, 7, 0); private final Color bg = new Color(216, 226, 220); private final Color displayColor = new Color(23, 48, 28); private final Color operandBtnColor = new Color(74, 37, 69); private final Color operatorBtnColor = new Color(247, 157, 92); private final Color equalBtnColor = new Color(5, 140, 66); private final Color delBtnColor = new Color(231, 29, 54); private void addDisplayField(){ displayField = new JTextField(); displayField.setPreferredSize(displayDimension); displayField.setHorizontalAlignment(SwingConstants.RIGHT); displayField.setBackground(displayColor); displayField.setForeground(Color.WHITE); displayField.setFont(new Font("Arial", Font.PLAIN, 24)); displayField.setBorder(BorderFactory.createEmptyBorder()); GridBagConstraints gbc = new GridBagConstraints(); // place in 1st col and 1st row gbc.gridx = 0; gbc.gridy = 0; getContentPane().add(displayField, gbc); } private JButton createBtn(String btnText, Dimension preferredSize, Color bgColor){ JButton btn = new JButton(btnText); btn.setPreferredSize(preferredSize); btn.setBackground(bgColor); btn.setForeground(Color.WHITE); btn.setFont(new Font("Arial", Font.PLAIN, 20)); btn.setBorder(BorderFactory.createEmptyBorder()); return btn; } private void addBtns(){ GridBagConstraints gbc = new GridBagConstraints(); zeroBtn = createBtn("0", btnDimension, operandBtnColor); gbc.gridx = 0; gbc.gridy = 0; gbc.insets = leftBtnInsets; btnsPanel.add(zeroBtn, gbc); pointBtn = createBtn(".", btnDimension, operandBtnColor); gbc.gridx = 1; gbc.insets = midBtnInsets; btnsPanel.add(pointBtn, gbc); exponentBtn = createBtn("^", btnDimension, operatorBtnColor); gbc.gridx = 2; gbc.insets = midBtnInsets; btnsPanel.add(exponentBtn, gbc); plusBtn = createBtn("+", btnDimension, operatorBtnColor); gbc.gridx = 3; gbc.insets = rightBtnInsets; btnsPanel.add(plusBtn, gbc); oneBtn = createBtn("1", btnDimension, operandBtnColor); gbc.gridx = 0; gbc.gridy = 1; gbc.insets = leftBtnInsets; btnsPanel.add(oneBtn, gbc); twoBtn = createBtn("2", btnDimension, operandBtnColor); gbc.gridx = 1; gbc.insets = midBtnInsets; btnsPanel.add(twoBtn, gbc); threeBtn = createBtn("3", btnDimension, operandBtnColor); gbc.gridx = 2; gbc.insets = midBtnInsets; btnsPanel.add(threeBtn, gbc); minusBtn = createBtn("-", btnDimension, operatorBtnColor); gbc.gridx = 3; gbc.insets = rightBtnInsets; btnsPanel.add(minusBtn, gbc); fourBtn = createBtn("4", btnDimension, operandBtnColor); gbc.gridx = 0; gbc.gridy = 2; gbc.insets = leftBtnInsets; btnsPanel.add(fourBtn, gbc); fiveBtn = createBtn("5", btnDimension, operandBtnColor); gbc.gridx = 1; gbc.insets = midBtnInsets; btnsPanel.add(fiveBtn, gbc); sixBtn = createBtn("6", btnDimension, operandBtnColor); gbc.gridx = 2; gbc.insets = midBtnInsets; btnsPanel.add(sixBtn, gbc); multiplyBtn = createBtn("*", btnDimension, operatorBtnColor); gbc.gridx = 3; gbc.insets = rightBtnInsets; btnsPanel.add(multiplyBtn, gbc); sevenBtn = createBtn("7", btnDimension, operandBtnColor); gbc.gridx = 0; gbc.gridy = 3; gbc.insets = leftBtnInsets; btnsPanel.add(sevenBtn, gbc); eightBtn = createBtn("8", btnDimension, operandBtnColor); gbc.gridx = 1; gbc.insets = midBtnInsets; btnsPanel.add(eightBtn, gbc); nineBtn = createBtn("9", btnDimension, operandBtnColor); gbc.gridx = 2; gbc.insets = midBtnInsets; btnsPanel.add(nineBtn, gbc); divideBtn = createBtn("/", btnDimension, operatorBtnColor); gbc.gridx = 3; gbc.insets = rightBtnInsets; btnsPanel.add(divideBtn, gbc); deleteBtn = createBtn("Del", btnDimension, delBtnColor); gbc.gridx = 0; gbc.gridy = 4; gbc.insets = leftBtnInsets; btnsPanel.add(deleteBtn, gbc); clearAllBtn = createBtn("CA", btnDimension, delBtnColor); gbc.gridx = 1; gbc.insets = midBtnInsets; btnsPanel.add(clearAllBtn, gbc); equalBtn = createBtn("=", btnDimension, equalBtnColor); gbc.gridx = 2; gbc.insets = rightBtnInsets; gbc.gridwidth = 3; gbc.fill = 1; btnsPanel.add(equalBtn, gbc); } private void addBtnsPanel(){ btnsPanel = new JPanel(); btnsPanel.setPreferredSize(btnPanelDimension); btnsPanel.setBackground(null); btnsPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); // place in 1st col and 2nd row gbc.gridx = 0; gbc.gridy = 1; getContentPane().add(btnsPanel, gbc); addBtns(); } public void initCalculatorGui(){ setSize(guiDimension); getContentPane().setBackground(bg); setDefaultCloseOperation(EXIT_ON_CLOSE); GridBagLayout gridBagLayout = new GridBagLayout(); setLayout(gridBagLayout); addDisplayField(); addBtnsPanel(); } public JTextField getDisplayField() { return displayField; } public void setDisplayField(JTextField displayField) { this.displayField = displayField; } public JPanel getBtnsPanel() { return btnsPanel; } public void setBtnsPanel(JPanel btnsPanel) { this.btnsPanel = btnsPanel; } public JButton getZeroBtn() { return zeroBtn; } public void setZeroBtn(JButton zeroBtn) { this.zeroBtn = zeroBtn; } public JButton getOneBtn() { return oneBtn; } public void setOneBtn(JButton oneBtn) { this.oneBtn = oneBtn; } public JButton getTwoBtn() { return twoBtn; } public void setTwoBtn(JButton twoBtn) { this.twoBtn = twoBtn; } public JButton getThreeBtn() { return threeBtn; } public void setThreeBtn(JButton threeBtn) { this.threeBtn = threeBtn; } public JButton getFourBtn() { return fourBtn; } public void setFourBtn(JButton fourBtn) { this.fourBtn = fourBtn; } public JButton getFiveBtn() { return fiveBtn; } public void setFiveBtn(JButton fiveBtn) { this.fiveBtn = fiveBtn; } public JButton getSixBtn() { return sixBtn; } public void setSixBtn(JButton sixBtn) { this.sixBtn = sixBtn; } public JButton getSevenBtn() { return sevenBtn; } public void setSevenBtn(JButton sevenBtn) { this.sevenBtn = sevenBtn; } public JButton getEightBtn() { return eightBtn; } public void setEightBtn(JButton eightBtn) { this.eightBtn = eightBtn; } public JButton getNineBtn() { return nineBtn; } public void setNineBtn(JButton nineBtn) { this.nineBtn = nineBtn; } public JButton getTenBtn() { return tenBtn; } public void setTenBtn(JButton tenBtn) { this.tenBtn = tenBtn; } public JButton getPointBtn() { return pointBtn; } public void setPointBtn(JButton pointBtn) { this.pointBtn = pointBtn; } public JButton getExponentBtn() { return exponentBtn; } public void setExponentBtn(JButton exponentBtn) { this.exponentBtn = exponentBtn; } public JButton getPlusBtn() { return plusBtn; } public void setPlusBtn(JButton plusBtn) { this.plusBtn = plusBtn; } public JButton getMinusBtn() { return minusBtn; } public void setMinusBtn(JButton minusBtn) { this.minusBtn = minusBtn; } public JButton getMultiplyBtn() { return multiplyBtn; } public void setMultiplyBtn(JButton multiplyBtn) { this.multiplyBtn = multiplyBtn; } public JButton getDivideBtn() { return divideBtn; } public void setDivideBtn(JButton divideBtn) { this.divideBtn = divideBtn; } public JButton getDeleteBtn() { return deleteBtn; } public void setDeleteBtn(JButton deleteBtn) { this.deleteBtn = deleteBtn; } public JButton getClearAllBtn() { return clearAllBtn; } public void setClearAllBtn(JButton clearAllBtn) { this.clearAllBtn = clearAllBtn; } public JButton getEqualBtn() { return equalBtn; } public void setEqualBtn(JButton equalBtn) { this.equalBtn = equalBtn; } public Dimension getGuiDimension() { return guiDimension; } public Dimension getDisplayDimension() { return displayDimension; } public Dimension getBtnPanelDimension() { return btnPanelDimension; } public Dimension getBtnDimension() { return btnDimension; } public Insets getLeftBtnInsets() { return leftBtnInsets; } public Insets getMidBtnInsets() { return midBtnInsets; } public Insets getRightBtnInsets() { return rightBtnInsets; } public Color getBg() { return bg; } public Color getDisplayColor() { return displayColor; } public Color getOperandBtnColor() { return operandBtnColor; } public Color getOperatorBtnColor() { return operatorBtnColor; } public Color getEqualBtnColor() { return equalBtnColor; } public Color getDelBtnColor() { return delBtnColor; } }
EventListeners
This class handles the logic for button presses, including calculating results based on user input. It converts infix expressions (like 1 + 2) to postfix notation (like 1 2 +) for easier evaluation. It uses a stack-based approach to evaluate the postfix expression and provides exception handling.
content_copy copypackage org.example; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import java.util.Stack; public class EventListeners implements ActionListener { private CalculatorGui calculatorGui; EventListeners(CalculatorGui calculatorGui){ this.calculatorGui = calculatorGui; } private int precedence(char ch){ if(ch == '^') return 3; else if(ch == '/' || ch == '*') return 2; else if(ch == '+' || ch == '-') return 1; else return 0; } private List<String> infixToPostfix(String infix){ List<String> postFixList = new ArrayList<>(); Stack<Character> operatorStack = new Stack<>(); // split input string with operators and also keep operators to simplify parsing of decimal numbers String[] infixArray = infix.split("(?<=[-+*/^])|(?=[-+*/^])"); for(int i = 0; i< infixArray.length; i++){ // add numbers in postFixList if(!infixArray[i].matches("[+\\-*/^]")){ postFixList.add(infixArray[i]); }else { // operator will always be a char at index 0 char operator = infixArray[i].charAt(0); // push operator in stack only if its precedence is higher than top operator in stack else pop operators until top operator has less precedence while(!operatorStack.isEmpty() && (precedence(operator) < precedence(operatorStack.peek()) || precedence(operator) == precedence(operatorStack.peek()))){ postFixList.add(operatorStack.pop().toString()); } operatorStack.push(operator); } } // after parsing whole string empty the stack and add in postFix while(!operatorStack.isEmpty()){ postFixList.add(operatorStack.pop().toString()); } return postFixList; } private String evaluatePostFix(List<String> postFixList){ Stack<Double> operandStack = new Stack<>(); try{ postFixList.forEach(p -> { // push operands in stack if(!p.matches("[+\\-*/^]")){ operandStack.push(Double.parseDouble(p)); }else{ // for operator: pop 2 operands and push result in stack Double operand1 = operandStack.pop(); Double operand2 = operandStack.pop(); if(p.equals("+")) operandStack.push(operand1 + operand2); else if(p.equals("-")) operandStack.push(operand2 - operand1); else if(p.equals("*")) operandStack.push(operand2 * operand1); else if(p.equals("/")) operandStack.push(operand2 / operand1); else if(p.equals("^")) operandStack.push(Math.pow(operand2, operand1)); } }); return operandStack.pop().toString(); }catch (Exception e) { return "Error"; } } @Override public void actionPerformed(ActionEvent e) { String displayText = calculatorGui.getDisplayField().getText(); if(e.getActionCommand().equals("Del")){ // remove last character if(!displayText.isEmpty()) calculatorGui.getDisplayField().setText(displayText.substring(0, displayText.length() -1 )); } else if(e.getActionCommand().equals("CA")){ // clear all text calculatorGui.getDisplayField().setText(""); } else if(e.getActionCommand().equals("=")){ // calculate result calculatorGui.getDisplayField().setText(evaluatePostFix(infixToPostfix(displayText))); } else{ // when operand is pressed append to display text if text does not contain Error if(displayText.contains("Error")) calculatorGui.getDisplayField().setText(e.getActionCommand()); else calculatorGui.getDisplayField().setText(displayText + e.getActionCommand()); } } }
This simple calculator application is a great way to understand the basics of Java Swing for GUI development, as well as fundamental concepts in parsing and evaluating mathematical expressions.