Search code examples
javaswingjbuttonactionlisteneractionevent

Calling ActionListener twice


I feel like there's an easy solution to this but for some reason, I can't wrap my head around it.

I have a situation where one button has to be clicked first in order for another one to be clicked. The options the user chooses for those 2 JButtons will determine the program's next step. This would mean I'd have to call actionListener twice, right? How would I do this in one actionPerformed method?

I don't think I could do if (e.getSource() == square[1][4] && e.getSource() == up) { } in the actionPerformed method...

EDIT: I have a board of 4x4 buttons that I made using a 2d array of JButtons and they contain 2 pieces. I also have 4 JButtons to represent north, east, south, west. The user has to click a piece on the 4x4 board and then click the direction in order for the piece to move.

public class Board extends JPanel implements actionListener
{
    // other variables listed

    private JButton[][] square;

    private boolean circleSelected = false;
    private boolean triangleSelected = false;
    
    // other irrelevant methods
    
    public void actionPerformed(actionEvent e)
    {
        // I don't know how to go about this part 
    }
}

If it's needed, I have square[0][0] containing the circle piece and square[2][3] containing the triangle piece. However, since the x and y will keep changing, I don't want to specify the exact placement. Instead, I'd rather determine the circleSelected variables from the image that's clicekd i.e. if the user clicks on the button[x][y] that has an image of a circle on it, then circleSelected = true if that makes sense...


Solution

  • This would mean I'd have to call actionListener twice, right?

    Not quite. You shouldn't directly call an ActionListener once but rather these listeners should respond to user action only.

    I don't think I could do if (e.getSource() == square[1][4] && e.getSource() == up) { } in the actionPerformed method...

    Correct, this would be very wrong since the ActionEvent's source property refers to one and only one object.


    Likely what you should do is use a private instance field of your class, or multiple fields, to hold state information that is used in your listeners. In other words, have a variable or variables hold information that tells the class if a button has been pressed previously, which button, etc, and then base what happens in a listener by what is held by these state variables. For more specific details and answers, please consider asking a more specific question, including pertinent code, preferably a minimal example program.

    As an aside, most buttons should have their own listener, often as an anonymous inner class.

    Aside #2: if a button should not be pressed until another button is pressed, then that button, or its Action, should be disabled until it is supposed to be pressed. This can be changed by calling .setEnabled(...) on the button or on its Action (if it has an Action).

    For example, try compiling and running the code below. Here I use firstButton as my state field, the one that changes the behavior of the ActionListener. You can use booleans, or any instance fields of the class for this purpose:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Board extends JPanel {
        public static final int ROWS = 4;
        private JButton[][] square = new JButton[ROWS][ROWS];
    
        private JButton firstButton = null;
        
        public Board() {
            JPanel squarePanel = new JPanel(new GridLayout(ROWS, ROWS));
            for (int i = 0; i < square.length; i++) {
                for (int j = 0; j < square.length; j++) {
                    String text = String.format("[%d, %d]", j, i);
                    square[i][j] = new JButton(text);
                    square[i][j].setFont(square[i][j].getFont().deriveFont(32f));
                    square[i][j].addActionListener(e -> squareListener(e));
                    squarePanel.add(square[i][j]);
                }
            }
            
            setLayout(new BorderLayout());
            add(squarePanel);
        }
        
        private void squareListener(ActionEvent e) {
            if (firstButton == null) {
                firstButton = (JButton) e.getSource();
                firstButton.setEnabled(false);
            } else {
                JButton secondButton = (JButton) e.getSource();
                secondButton.setEnabled(false);
                String text = "First Button: " + firstButton.getActionCommand() + "; Second Button: " + secondButton.getActionCommand();
                JOptionPane.showMessageDialog(this, text, "Selected Buttons", JOptionPane.PLAIN_MESSAGE);
                for (JButton[] row : square) {
                    for (JButton button : row) {
                        button.setEnabled(true);
                    }
                }
                firstButton = null;
            }
        }
        
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                JFrame frame = new JFrame("GUI");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Board());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        }
    }