Simple Calculator - Java
Simple Calculator - Java
Structure : Three files: main, GUI (subclass of JFrame), logic. Components: JButton, JTextField (right justified). Containers: JFrame, several JPanels. Layouts : BorderLayout to put the other panels together. Two GridLayout panels for the buttons. Listeners : One ActionListener which is shared by all numeric key buttons. Similarly share an ActionListener for all operator buttons. ActionListener for Clear button. : Use Font to enlarge font for components. : try...catch for NumberFormatExceptions.
// Possible enhancements: // // // // // // Check for zero before division. Additional operations: mod, square root, sign change, ... Make this work with doubles, BigInteger, or ... Format double results with DecimalFormat Add keyboard listener. Change to RPN (Reverse Polish Notation)
/** calc-ui-model/CalcGUI.java - A GUI for the calculator. * @author Fred Swartz * @version 2004-04-20 Rodenbach, 2007-02-11 minor changes.
///////////////////////////////////////////////////////////////////// class Calc class Calc extends JFrame { //================================================= =============== constants private static final Font BIGGER_FONT = new Font("monspaced", Font.PLAIN, 20);
//================================================= ================== fields //... Component referenced during execution private JTextField _displayField; // display result / input.
//... Variables representing state of the calculator private boolean _startNumber = true; private String _previousOp = "="; // true: num key next // previous operation
//================================================= ============= method main public static void main(String[] args) { //... Set the Look and Feel to that of system we're running on. try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception unused) { ; // Ignore exception because we can't do anything. Will use default. }
//... Create the window. Calc window = new Calc(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setVisible(true); }
//================================================= ============= constructor public Calc() { //... Set attributes of the display field _displayField = new JTextField("0", 12); _displayField.setHorizontalAlignment(JTextField.RIGHT); _displayField.setFont(BIGGER_FONT);
//... Create and set attributes of clear button JButton clearButton = new JButton("Clear"); clearButton.setFont(BIGGER_FONT); clearButton.addActionListener(new ClearListener());
//... Use one listener for all numeric keys. ActionListener numListener = new NumListener();
//... Layout numeric keys in a grid. Generate the buttons // in a loop from the chars in a string.
String buttonOrder = "789456123 0 "; JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(5, 3, 2, 2)); for (int i = 0; i < buttonOrder.length(); i++) { String keyTop = buttonOrder.substring(i, i+1); JButton b = new JButton(keyTop); if (keyTop.equals(" ")) { //... Put a dummy button in this position. b.setEnabled(false); } else { //... Put a digit button in the interface. b.addActionListener(numListener); b.setFont(BIGGER_FONT); } buttonPanel.add(b); }
//... One ActionListener to use for all operator buttons. ActionListener opListener = new OpListener();
//... Create panel with gridlayout to hold operator buttons. // Use array of button names to create buttons in a loop.
String[] opOrder = {"+", "-", "*", "/", "="}; for (int i = 0; i < opOrder.length; i++) { JButton b = new JButton(opOrder[i]); b.addActionListener(opListener); b.setFont(BIGGER_FONT); opPanel.add(b); }
//... Put Clear button in flow layout to keep from expanding. JPanel clearPanel = new JPanel(); clearPanel.setLayout(new FlowLayout()); clearPanel.add(clearButton);
//... Layout the top-level content panel. JPanel content = new JPanel(); content.setLayout(new BorderLayout(5, 5)); content.add(_displayField, BorderLayout.NORTH ); content.add(buttonPanel , BorderLayout.CENTER); content.add(opPanel content.add(clearPanel , BorderLayout.EAST ); , BorderLayout.SOUTH );
content.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//================================================= ============= actionClear /** Called by Clear btn action listener and elsewhere.*/ private void actionClear() { _startNumber = true; _displayField.setText("0"); _previousOp = "="; _logic.setTotal("0"); } // Expecting number, not op.
//////////////////////////////////////////// inner listener class OpListener /** Listener for all op buttons. */ class OpListener implements ActionListener { public void actionPerformed(ActionEvent e) { // The calculator is always in one of two states. // 1. A number must be entered -- an operator is wrong. // 2. An operator must be entered. if (_startNumber) { // Error: needed number, not operator //... In this state we're expecting a number, but got an operator. actionClear(); _displayField.setText("ERROR - No operator");
} else { //... We're expecting an operator. _startNumber = true; // Next thing must be a number try { // Get value from display field, convert, do prev op // If this is the first op, _previousOp will be =. String displayText = _displayField.getText();
if (_previousOp.equals("=")) { _logic.setTotal(displayText); } else if (_previousOp.equals("+")) { _logic.add(displayText); } else if (_previousOp.equals("-")) { _logic.subtract(displayText); } else if (_previousOp.equals("*")) { _logic.multiply(displayText); } else if (_previousOp.equals("/")) { _logic.divide(displayText); }
_displayField.setText("" + _logic.getTotalString());
//... set _previousOp for the next operator. _previousOp = e.getActionCommand(); }//endif _startNumber }//endmethod }//end class
//////////////////////////////////// inner listener class ClearListener /** Action listener for numeric keys */ class NumListener implements ActionListener { public void actionPerformed(ActionEvent e) { String digit = e.getActionCommand(); // Get text from button if (_startNumber) { //... This is the first digit, clear field and set _displayField.setText(digit); _startNumber = false; } else { //... Add this digit to the end of the display field _displayField.setText(_displayField.getText() + digit); } } }
The logic/model // File : calc-ui-model/CalcLogic.java - The logic of a calculator. // Description: This is the logic/model of a calculator. // // It has no user interface, but could be called from either a GUI or console user interface.
// Separating the model (logic) from the interface has advantages. // In this program the model is small, so it may not be as obvious,
// but in larger programs the advantages can be substantial. // 1. It is simpler for the developer to work with. // 2. It can be used with many kinds of interfaces without changes. Eg, // a GUI interface, a command-line interface, or a web-based interface.
// 3. The model can be changed (eg, to work with BigInteger) without // // // // Author : Fred Swartz - 2004-11-17 + 2007-02-13 - Placed in public domain. changing the user interface. Of course, some changes require interface changes, but the separation makes this easier.
// Possible enhancements: // * Change numbers to double, or BigInteger, or even Roman numerals! // This should be possible without the user interface knowing much // about the change (except perhaps to add a "." for floating-point input). // * Add error checking (eg, division by zero checking. // How would you communicate an error to the caller? Ans: Exceptions. // * Additional operations - change sign, mod, square root, ...
//-- Instance variables. private int _currentTotal; // The current total is all we need to remember.
_currentTotal = 0; }