Four Function Calculator using Java and Swing

by Kam-Hung Soh (kamhung dot soh at gmail dot com) 2009/05/24 03:59:47

Index Copyright About Blog

Introduction

Calculator

This article describes implementing a four-function calculator application in Java 1.6 and Swing, which is a common exercise to write a application slightly more complex than Hello World.

Architecture and Implementation

Overview

The calculator is window with a menu bar, single-line text display and buttons. The user can only enter data using the buttons in the display and keyboard input is disabled. The text display and buttons are laid out in a tabular format. The menu bar has File / Exit and Help / About menu items.

Folder Structure

To facilitate discussion, below is the folder structure of the source code:

./src:
infixcalculator  resources

./src/infixcalculator:
AboutAction.java  HelpAction.java       InfixCalculatorResource.java
ExitAction.java   images                Main.java
FileAction.java   InfixCalculator.java  Model.java

./src/infixcalculator/images:
InfixCalculator_32x32.gif  InfixCalculator_32x32.xcf

./src/resources:
InfixCalculator.properties

Model-View-Controller

This application roughly follows the model-view-controller (MVC) pattern. The model (current result, argument and operator) is stored in the Model class, while the view and controller are implemented in the InfixCalculator class.

The Model is very simple and there is little to say about it. Most of the complexity in this exercise is in the controller which processes the user's input (the actionPerformed() method). The text display (view) is updated in doEdit() when the user enters a number, while the model is updated in doEvaluate() when the user enters an operator (see code below from InfixCalculator.java).

   126    public void actionPerformed(ActionEvent e) {
   127      String input = e.getActionCommand();
   128      String buffer = jtf.getText();
   129
   130      if (input.matches(RE_EDIT)) {
   131        buffer = doEdit(buffer, input);
   132      } else if (input.matches(RE_EVALUATE)) {
   133        buffer = doEvaluate(buffer, input);
   134      }
   135      previousInput = input;
   136      jtf.setText(buffer);
   137    }

There is some coupling between doEdit() and doEvaluate to make the application work like a real four-function calculator:

resetText
The result is shown after the user enters an operator. If the user enters 1 + 3 + 5, then 4 is shown after the second + entry.
textChanged
The result is shown when the user hits the equal button more than once. If the user enters 1 + 2 = =, then 5 is shown after the second = entry.
previousInput
The result should not be re-evaluated when the user hits an operator button after the equal button. If the user enters 1 + 3 = + 5 =, then the result should be 4 after the = and second + entry.

Menu Items and AbstractActions

The menus and menu items are defined by extending the AbstractAction class. You can encapsulate standard items such as the name, description and shortcut key in an Action. The menu bar displays these actions with some text, a tooltip and underlines the shortcut key. If you also define a toolbar with icons and the same actions, the toolbar should display an icon and a tooltip.

For instance, FileAction.java below defines the File menu. Note that the definition of various fields are obtained from a resource bundle (lines 11 to 14).

     1  package infixcalculator;
     2
     3  import java.awt.event.ActionEvent;
     4  import javax.swing.AbstractAction;
     5  import javax.swing.Action;
     6  import java.util.ResourceBundle;
     7
     8  class FileAction extends AbstractAction {
     9    public FileAction() {
    10      super();
    11      ResourceBundle res = InfixCalculatorResource.getInstance();
    12      putValue(Action.NAME, res.getString("FileAction.Name"));
    13      putValue(Action.SHORT_DESCRIPTION, res.getString("FileAction.Description"));
    14      putValue(Action.MNEMONIC_KEY, new Integer(res.getString("FileAction.Mnemonic")));
    15    }
    16    public void actionPerformed(ActionEvent e) {
    17    }
    18  }

Below are the resource strings used by FileAction in InfixCalculator.properties.

     3  FileAction.Name = File
     4  FileAction.Description = File
     5  # java.awt.event.KeyEvent.VK_F
     6  FileAction.Mnemonic = 70

Here's how the FileAction is added to the menu bar in InfixCalculator.java:

    72      JMenuBar menuBar = new JMenuBar();
    73      getRootPane().setJMenuBar(menuBar);
    74
    75      JMenu menu = new JMenu(new FileAction());
    76      menuBar.add(menu);

Layout

The layout of the single-line text display and buttons are controlled by a GridBagLayout (essentially a table). The text display occupies the entire top row and the buttons occupy each cell in a 4 by 5 grid below the text display. Below is a rough ASCII sketch of the cells in a table (T = text display, D = delete, C = clear entry, A = clear all, N = negate):

  0 1 2 3 4
0 T T T T T
1 7 8 9 + D
2 4 5 6 - C
3 1 2 3 * A
4 0 . N / =

For simplicity, each button occupies only one cell and you only need a name to define a button. In lines 106 to 122 of InfixCalculator.java, we define an array of button names and use a loop to create each button and set its gridx and gridy position in the table. Like the menus and menu items, the values are read from a resource bundle (lines 116 to 120 in InfixCalculator.java).

   106      String[] buttons = {
   107        "Button7", "Button8", "Button9", "ButtonPlus", "ButtonBackspace",
   108        "Button4", "Button5", "Button6", "ButtonMinus", "ButtonClearEntry",
   109        "Button1", "Button2", "Button3", "ButtonMultiply", "ButtonClearAll",
   110        "Button0", "ButtonPoint", "ButtonNegate", "ButtonDivide", "ButtonEvaluate"
   111      };
   112      for (int i = 0; i < buttons.length; ++i) {
   113        String name = buttons[i];
   114        JButton button = new JButton();
   115        button.setName(name);
   116        button.setText(res.getString(name + ".Text"));
   117        button.setToolTipText(res.getString(name + ".ToolTipText"));
   118        button.addActionListener(this);
   119        gbc.gridx = Integer.parseInt(res.getString(name + ".gridx"));
   120        gbc.gridy = Integer.parseInt(res.getString(name + ".gridy"));
   121        gridbag.setConstraints(button, gbc);
   122        cp.add(button);

Here's an example of the resources for one button in InfixCalculator.properties:

    26  Button7.Text = 7
    27  Button7.ToolTipText = 7
    28  Button7.gridx = 0
    29  Button7.gridy = 1

Resources and Customization

Phone Keypad button arrangement.

This application uses a run-time resource bundle stored in resources/InfixCalculator.properties file to define many properties of the application, such as the window title, menu item names and button positions. The resource bundle is initialized in InfixCalculatorResource.java. Usually, resource bundles are described in the context of localization (or l10n) but it is straightforward (if a little tedious) to define other simple properties.

A .properties file is read when the application is started, so you can change the appearance of the application by editing the .properties file in the JAR archive. For instance, you can arrange the buttons in the same order as a phone keypad (123 in the top row and 789 in the bottom row) by changing the appropriate gridy values. Of course, customization is limited by the functionality that is implemented.

Concluding Remarks

A simple four-function calculator was implemented in Java and Swing. A Java resource bundle was used to simplify building the layout of the calculator's buttons and provide simple customization feature for the user. Swing's AbstractAction was used to encapsulate properties of actions in the menu bar.

Some improvements that can be made in future projects:

References

Index Copyright About Blog