6
\$\begingroup\$

I am new to Java Swing and have decided to create a calculator to learn some of the basic. I would like someone to look over my code to see if they can give me any improvements.

I have 6 classes and 2 interfaces. I have chosen to split the communication of both operators and digits. The mainframe is the controller and actually creates the objects. The other classes are the different components.

MainFrame (creates all objects):

package calculator;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.ScriptEngine;

public class MainFrame extends JFrame {

    private FormArea formpanel;
    private OutputArea outputText;
    private WorkoutExpression workingOutMathExpression;
    private static String mathmaticalExpression = "";

    public MainFrame() {
        super("Calculator");
        setVisible(true);
        setSize(800, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        formpanel = new FormArea();
        outputText = new OutputArea();
        workingOutMathExpression = new WorkoutExpression();

        setLayout(new BorderLayout());

        formpanel.setStringListener(new DigitListener() {

            public void StringEmmiter(String text) {
                outputText.addText(text);
                mathmaticalExpression = mathmaticalExpression + text;
                //System.out.println(mathmaticalExpression);
                WorkoutExpression.setMathExpression(mathmaticalExpression);

            }
        });

        formpanel.setOperatorListener(new MathOperatorListener() {

            public void OperatorEmitter(String text2a) {
                if (text2a.equals("CLEAR")) {
                    outputText.refreshTextArea();
                    mathmaticalExpression = "";
                    workingOutMathExpression.resetMathExpression();

                } else if (text2a.equals("CLEAR") == false) {
                    outputText.addText(text2a);

                    if (text2a.equals("=")) {
                        try {
                            outputText.addText(workingOutMathExpression.getCalculationOfExpression());
                        } catch (ScriptException e) {
                            e.printStackTrace();
                        }

                    } else {
                        mathmaticalExpression = mathmaticalExpression + text2a;
                        //System.out.println(mathmaticalExpression);
                        WorkoutExpression.setMathExpression(mathmaticalExpression);
                    }

                }

            }
        });

        add(formpanel, BorderLayout.WEST);
        add(outputText, BorderLayout.CENTER);

    }
}

Formpanel is the panel which contains all the buttons:

package calculator;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class FormArea extends JPanel implements ActionListener {
    private ButtonTemplate No1;
    private ButtonTemplate No2;
    private ButtonTemplate No3;
    private ButtonTemplate No4;
    private ButtonTemplate No5;
    private ButtonTemplate No6;
    private ButtonTemplate No7;
    private ButtonTemplate No8;
    private ButtonTemplate No9;
    private ButtonTemplate No0;
    private ButtonTemplate addition;
    private ButtonTemplate substraction;
    private ButtonTemplate division;
    private ButtonTemplate total;
    private ButtonTemplate multiplication;
    private JButton clearButton;
    private ButtonTemplate decimalPoint;

    private DigitListener digitListener;
    private MathOperatorListener operatorListener;

    public FormArea() {
        // SetSize
        setPreferredSize(new Dimension(300, 500));
        // Create all Objects
        No1 = new ButtonTemplate("1");
        No2 = new ButtonTemplate("2");
        No3 = new ButtonTemplate("3");
        No4 = new ButtonTemplate("4");
        No5 = new ButtonTemplate("5");
        No6 = new ButtonTemplate("6");
        No7 = new ButtonTemplate("7");
        No8 = new ButtonTemplate("8");
        No9 = new ButtonTemplate("9");
        No0 = new ButtonTemplate("0");
        addition = new ButtonTemplate("+");
        substraction = new ButtonTemplate("-");
        division = new ButtonTemplate("/");
        total = new ButtonTemplate("=");
        multiplication = new ButtonTemplate("*");
        clearButton = new JButton("CLEAR");
        decimalPoint = new ButtonTemplate(".");

        clearButton.setPreferredSize(new Dimension(100, 60));

        setLayout(new GridBagLayout());

        GridBagConstraints gc = new GridBagConstraints();

        // Layout

        // Column 1
        gc.gridx = 0;
        gc.gridy = 0;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No1, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No4, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No7, gc);

        // Column 2
        gc.gridx = 1;
        gc.gridy = 0;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No2, gc);

        gc.gridx = 1;
        gc.gridy = 1;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No5, gc);

        gc.gridx = 1;
        gc.gridy = 2;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No8, gc);

        // Column3
        gc.gridx = 2;
        gc.gridy = 0;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No3, gc);

        gc.gridx = 2;
        gc.gridy = 1;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No6, gc);

        gc.gridx = 2;
        gc.gridy = 2;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No9, gc);

        // Operators

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(addition, gc);

        gc.gridx = 1;
        gc.gridy = 3;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(No0, gc);

        gc.gridx = 2;
        gc.gridy = 3;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(substraction, gc);

        // Operator 2

        gc.gridx = 0;
        gc.gridy = 4;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(division, gc);

        gc.gridx = 1;
        gc.gridy = 4;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(multiplication, gc);

        gc.gridx = 2;
        gc.gridy = 4;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(total, gc);

        gc.gridx = 1;
        gc.gridy = 5;
        gc.weightx = 0.1;
        gc.weighty = 5;
        add(clearButton, gc);

        gc.gridx = 0;
        gc.gridy = 5;
        gc.weightx = 0.1;
        gc.weighty = 0.1;
        add(decimalPoint, gc);


        // ActionButton
        No1.addActionListener(this);
        No2.addActionListener(this);
        No3.addActionListener(this);
        No4.addActionListener(this);
        No5.addActionListener(this);
        No6.addActionListener(this);
        No7.addActionListener(this);
        No8.addActionListener(this);
        No9.addActionListener(this);
        No0.addActionListener(this);

        ArrayList<ButtonTemplate> operatorsArrayList = 
        new ArrayList<ButtonTemplate>(Arrays.asList(iterateThroughOperators()));

        for (ButtonTemplate btn : operatorsArrayList) {
            btn.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    ButtonTemplate source1 = (ButtonTemplate) e.getSource();

                    if (source1 == btn) {
                        operatorListener.OperatorEmitter(btn.getText());

                    }
                }
            });
        }

        clearButton.addActionListener(new ActionListener() {


            public void actionPerformed(ActionEvent e) {
                JButton s1 =  (JButton) e.getSource();

                operatorListener.OperatorEmitter(s1.getText());

            }
        });

    }

    public void setStringListener(DigitListener digitlistener) {
        this.digitListener = digitlistener;
    }

    public void setOperatorListener(MathOperatorListener operatorListener) {
        this.operatorListener = operatorListener;
    }

    public ButtonTemplate[] iterateThroughButton() {
        ButtonTemplate btn[] = { No1, No2, No3, No4, No5, No6, No7, No8, No9, No0 };
        return btn;
    }

    public ButtonTemplate[] iterateThroughOperators() {
        ButtonTemplate operatorBtn[] = { addition, substraction, division, total, multiplication,decimalPoint
                };

        return operatorBtn;
    }

    public void actionPerformed(ActionEvent e) {
        JButton source = (JButton) e.getSource();

        for (ButtonTemplate SingleButton : iterateThroughButton()) {
            if (source == SingleButton) {

                digitListener.StringEmmiter(SingleButton.getText());

            }
        }
    }
}

Button Template is just a standard button with a specified size:

package calculator;

import java.awt.Dimension;

import javax.swing.JButton;

public class ButtonTemplate extends JButton{
    private String nameOfTheButton;

    public ButtonTemplate(String nameOfButton){
        this.nameOfTheButton = nameOfButton;
        this.setText(nameOfTheButton);
        this.setPreferredSize(new Dimension(70,50));

    }


}

DigitListener listens for the digits and sends them to the mainframe:

package calculator;

public interface DigitListener {
    public void StringEmmiter(String text1);
}

Operator listener listens to the operators and sends them to the mainframe:

package calculator;

public interface MathOperatorListener {
    public void OperatorEmitter(String text2);

}

MathExpression works out the mathematical expression using scriptengine:

package calculator;

import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.ScriptEngine;

public class WorkoutExpression {

    private static String mathExpression;

    public static void setMathExpression(String mathExpression) {
        WorkoutExpression.mathExpression = mathExpression;
    }
    public String getCalculationOfExpression() throws ScriptException{
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine engine = mgr.getEngineByName("JavaScript");
        return  engine.eval(mathExpression).toString();

    }
    public void resetMathExpression(){
        mathExpression = "0";
    }



}

Output area is whether the calculation is displayed:

package calculator;

import java.awt.BorderLayout;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class OutputArea extends JPanel {
    private JTextArea textarea;

    public OutputArea() {
        textarea = new JTextArea();

        setLayout(new BorderLayout());

        add(new JScrollPane(textarea));
    }

    public void addText(String text){
        textarea.append(text);
    }
    public void refreshTextArea(){
        textarea.setText(null);
    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to CodeReview! A Github link to your project, while not needed, would make testing easier for the reviewers allowing better quality reviews. \$\endgroup\$ – Caridorc Aug 27 '15 at 12:39
3
\$\begingroup\$

Use Arrays

At a glance, your creating and instantiating the ButtonTemplate objects could be done as an array.

e.g.

private ButtonTemplate[] buttons = new ButtonTemplate[10];

It would allow you to use a simple loop to instantiate, and add the ActionListener, e.g.

for (int i = 0; i < buttons.length; i++) {
  buttons[i] = new ButtonTemplate(Integer.toString(i));
  buttons[i].addActionListener(this);
}

Employ Lambda Expressions

If you have access to Java 8, You can use lambda expressions to simply the code where you use Functional Interfaces, e.g.

instead of:

btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ButtonTemplate source1 = (ButtonTemplate) e.getSource();

                   if (source1 == btn) {
                       operatorListener.OperatorEmitter(btn.getText());
                    }
               }
            });

You can simply write:

btn.addActionListener(e -> {
  if ((ButtonTemplate)e.getSource() == btn) {
    operatorListener.OperatorEmitter(btn.getText());
  }
});

I also use e.getSource() directly here rather than storing it as a variable since we're only doing one thing with it anyway, but feel free to use it your way if you find it makes it more understandable/readable.

Read up on what I linked, but just as an additional example, your MathOperatorListener is a functional interface. So this section where you call setOperatorListener

formpanel.setOperatorListener(new MathOperatorListener() {
            public void OperatorEmitter(String text2a) {

Can be replaced with:

formpanel.setOperatorListener(e -> {
  // content
});

The e is just a variable used to refer to the text2a that is passed. You can name it anything. Same deal of course for your DigitListener interface. You may start using the @FunctionalInterface annotation so the compiler would check and confirm for you, albeit Functional Interfaces are easily identifiable since they only have a single method).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.