Question

I am having trouble implementing multiple action listeners in my Java program. The program is a calculator that does basic functions with a basic GUI. From my research I have noticed that action listeners should go into different classes. I have comprised my program of four action listeners but I keep getting a terrible amount of errors every time I run my program along with warnings for each action listener saying "The serializable class ClearListener does not declare a static final serialVersionUID field of type long" Any help with this would be greatly appreciated.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;


public class GUICalc extends JPanel {

    private static final long serialVersionUID = 1L;

    JButton[] numberButtons;
    JButton[] upButtons;

    JTextField field;
    double num1;

    double num2;

    double ans; 
    int op;

    DigitalListener listener1 = new DigitalListener();
    OperatorListener listener2 = new OperatorListener();
    ClearListener listerner3 = new ClearListener();
    EqualsListener listerner4 = new EqualsListener();

    // 0 = gridx 1 gridy 2 gridwidth 3 gridheight
    private int[][] numConstraints = new int[][] {
            {0, 4, 1, 1},
            {0, 1, 1, 1},
            {1, 1, 1, 1},
            {2, 1, 1, 1},
            {0, 2, 1, 1},
            {1, 2, 1, 1},
            {2, 2, 1, 1},
            {0, 3, 1, 1},
            {1, 3, 1, 1},
            {2, 3, 1, 1},
    };

    private int[][] upConstraints = new int[][] {
            {1, 4, 1, 1},
            {3, 4, 1, 1},
            {3, 3, 1, 1},
            {3, 2, 1, 1},
            {3, 1, 1, 1},
            {0, 5, 4, 1},
            {2, 4, 1, 1},
    };

    public GUICalc() {
        setPreferredSize(new Dimension(666, 666)); //width & heigth

        GridBagLayout layout; // used to be private
        GridBagConstraints gbc; // used to be private

        layout = new GridBagLayout();
        layout.columnWidths = new int[] {60, 60, 60, 60};
        layout.rowHeights = new int[] {60, 60, 60, 60, 60, 60};
        setLayout(layout);

        gbc = new GridBagConstraints();

            numberButtons = new JButton[10];

            numberButtons[0] = new JButton("0");
            numberButtons[1] = new JButton("1");
            numberButtons[2] = new JButton("2");
            numberButtons[3] = new JButton("3");
            numberButtons[4] = new JButton("4");
            numberButtons[5] = new JButton("5");
            numberButtons[6] = new JButton("6");
            numberButtons[7] = new JButton("7");
            numberButtons[8] = new JButton("8");
            numberButtons[9] = new JButton("9");

            for(int i = 0; i < numberButtons.length; i++){
                gbc.gridx = numConstraints[i][0];
                gbc.gridy = numConstraints[i][1];
                gbc.gridwidth = numConstraints[i][2];
                gbc.gridheight = numConstraints[1][3];
                gbc.fill = GridBagConstraints.BOTH;
                gbc.insets = new Insets(2, 2, 2, 2); 
                numberButtons[i].addActionListener(listener1);
                add(numberButtons[i], gbc);
                }

        upButtons = new JButton[7];

        upButtons[0] = new JButton(".");
        upButtons[1] = new JButton("/");
        upButtons[2] = new JButton("*");
        upButtons[3] = new JButton("-");
        upButtons[4] = new JButton("+");
        upButtons[5] = new JButton("=");
        upButtons[6] = new JButton("C");

            gbc.gridx = upConstraints[0][0];
            gbc.gridy = upConstraints[0][1];
            gbc.gridwidth = upConstraints[0][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[0].addActionListener(listener2);
            add(upButtons[0], gbc); 

            gbc.gridx = upConstraints[1][0];
            gbc.gridy = upConstraints[1][1];
            gbc.gridwidth = upConstraints[1][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[1].addActionListener(listener2);
            add(upButtons[1], gbc); 

            gbc.gridx = upConstraints[2][0];
            gbc.gridy = upConstraints[2][1];
            gbc.gridwidth = upConstraints[2][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[2].addActionListener(listener2);
            add(upButtons[2], gbc); 

            gbc.gridx = upConstraints[3][0];
            gbc.gridy = upConstraints[3][1];
            gbc.gridwidth = upConstraints[3][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[3].addActionListener(listener2);
            add(upButtons[3], gbc); 

            gbc.gridx = upConstraints[4][0];
            gbc.gridy = upConstraints[4][1];
            gbc.gridwidth = upConstraints[4][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[4].addActionListener(listener2);
            add(upButtons[4], gbc); 

            gbc.gridx = upConstraints[5][0];
            gbc.gridy = upConstraints[5][1];
            gbc.gridwidth = upConstraints[5][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[5].addActionListener(listerner4);
            add(upButtons[5], gbc); 

            gbc.gridx = upConstraints[6][0];
            gbc.gridy = upConstraints[6][1];
            gbc.gridwidth = upConstraints[6][2];
            gbc.gridheight = upConstraints[1][3];
            gbc.insets = new Insets(2, 2, 2, 2);
            upButtons[6].addActionListener(listerner3);
            add(upButtons[6], gbc);

        field = new JTextField();
        field.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        field.setEditable(false);
        field.setFont(new Font("Arial",Font.PLAIN, 24));
        field.setText(null);

        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 4;
        gbc.gridheight = 1;

        add(field, gbc);

    }


    public static void main(String [] args){
        JFrame frame = new JFrame("Calculator");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLayout(new BorderLayout());
        frame.add(new GUICalc(), BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);     

    }

}

class DigitalListener extends GUICalc implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == numberButtons[0]){
            field.setText(field.getText() + 0);
            }
            if(e.getSource() == numberButtons[1]){
            field.setText(field.getText() + 1);
            }
            if(e.getSource() == numberButtons[2]){
            field.setText(field.getText() + 2);
            }
            if(e.getSource() == numberButtons[3]){
            field.setText(field.getText() + 3);
            }
            if(e.getSource() == numberButtons[4]){
            field.setText(field.getText() + 4);
            }
            if(e.getSource() == numberButtons[5]){
            field.setText(field.getText() + 5);
            }
            if(e.getSource() == numberButtons[6]){
            field.setText(field.getText() + 6);
            }
            if(e.getSource() == numberButtons[7]){
            field.setText(field.getText() + 7);
            }
            if(e.getSource() == numberButtons[8]){
            field.setText(field.getText() + 8);
            }
            if(e.getSource() == numberButtons[9]){
            field.setText(field.getText() + 9);
            }
            if(e.getSource() == upButtons[0] && !field.getText().contains(".")) {
                field.setText(field.getText() + ".");
            }
    }
}

class OperatorListener extends GUICalc implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e){
        if(e.getSource() == upButtons[1]) {
            num1 = Integer.parseInt(field.getText());
            op = 4;
            field.setText("");
        }

        if(e.getSource() == upButtons[2]) {
            num1 = Integer.parseInt(field.getText());
            op = 3;
            field.setText("");
        }
        if(e.getSource() == upButtons[3]) {
            num1 = Integer.parseInt(field.getText());
            op = 2;
            field.setText("");
        }
        if(e.getSource() == upButtons[4]) {
            num1 = Integer.parseInt(field.getText());
            op = 1;
            field.setText("");
        }
    }
}

class ClearListener extends GUICalc implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == upButtons[6]) {
            field.setText("0.0");
        }
    }
}

class EqualsListener extends GUICalc implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == upButtons[5]) {
            num2 = Integer.parseInt(field.getText());

            if(op == 1){
                ans = num1 + num2;
            } else if(op == 2){
                ans = num1 - num2;

            } else if(op == 3){
                ans = num1 * num2;
            } else if(op == 4){
                ans = num1 / num2;
            } 

            op = 0;
            field.setText("" + ans);
        }
    }
}
Was it helpful?

Solution

Most probably you are getting Exception in thread "main" java.lang.StackOverflowError

Do in this way:

class OperatorListener implements ActionListener {

   private GUICalc guiCalc;
   public OperatorListener(GUICalc guiCalc){
       this.guiCalc=guiCalc;
   }
   // use guiCalc to refer the values
   ...
 }

and in GUICalc.java

OperatorListener listener2 = new OperatorListener(this);

Do same thing for all type of your custom listener.


You have created a cyclic dependencies between objects.

GUICalc contains OperatorListener

public class GUICalc extends JPanel {
   ...

   OperatorListener listener2 = new OperatorListener();
   ...

}

OperatorListener is extending GUICalc class

class OperatorListener extends GUICalc implements ActionListener {...}

Now whenever GUICalc super class object is going to construct it tries to create the dependencies sub-class objects such as OperatorListener but now sub-class object can't be constructed until and unless super-class object is created.

OTHER TIPS

Although the first answer was already accepted, some hints:

The recommendation that "action listeners should be in different classes" is correct. However, this does not mean that you should arbitrarily combine several ActionListeners into one class. While this is possible and may be feasible in some cases, there are solutions that are more appropriate for use-cases like yours.

This could help you to simplify your code a little. (If I had to clean it up, I'd start with the GridBagLayout and these numConstraints arrays - I think these are horrible - but this is a different story...). Particularly, you could create a method that creates an anonymous ActionListener like this:

private ActionListener createActionListener(final String string)
{
    return new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            field.setText(field.getText() + string);
        }
    };
}

Then you could create instances of these listeners and add them to the buttons:

for(int i = 0; i < numberButtons.length; i++)
{ 
    ...
    numberButtons[i].addActionListener(
        createActionListener(String.valueOf(i)));
}

(Note that then you might not even have to store the numberButtons array as an instance variable in the best case...)

A similar concept could be applied to the operation-buttons. In general, I consider it as a good practice to use anonymous listeners to just "connect" a particular method call to one button, roughly like this:

someButton.addActionListener(new ActionListener()
{
    @Override
    public void actionPerformed(ActionEvent e)
    {
        doSomething();
    }
});

For many cases, it is even more beneficial to use Actions (see http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html ), but this is at least conceptually similar, in contrast to having an ActionListener that examines the event source or the action command.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top