0

This is a difficult ask, but how do I add an ActionListener to multiple buttons that I have created in a for loop?

Here's my code so far:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class Loop extends JFrame {

    public Loop() {
        this.setSize(700, 300);
        this.setLocation(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JPanel jpanel = new JPanel();
        jpanel.setBackground(Color.CYAN);
        jpanel.setLayout(new GridLayout(0,6));
        
        JButton[] jButton = new JButton[6];
        String string[] = {"One","Two","Three","Four","Five","Six"};
        
        this.add(jpanel);
        int j=0;
        for(int i =0; i<jButton.length;i++) {
            while(j < 6) {
            jButton[i]= new JButton();
            jButton[i].setPreferredSize(new Dimension(50, 50));
            jButton[i].setText(string[j++]);
            
            jpanel.add(jButton[i]);
            jButton[i].addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    System.out.println("Works");
                }
            });
            }
        }   
    }
    
    public static void main(String[] args) {
        Loop myLoop = new Loop();
        myLoop.setVisible(true);
    }
}

(It seems from the code that to add the ActionListener in a for loop gives all the buttons the same action. Problem is I want the actions to be different for each button).

Any response would be greatly appreciated.

Abra
  • 19,142
  • 7
  • 29
  • 41
  • All of your action listeners do the same thing and print "works". What do you want each button to do? If you're going to use a loop, you have to choose the actions based on a loop. – matt Mar 17 '21 at 20:31
  • *Problem is I want the actions to be different for each button* - If the Action is different, the you should create a separate ActionListener for each button. You should NOT use `switch` logic in an ActionListener. However, if the Action is similar but not exact, then you can create a common listener to be shared by all buttons. See: https://stackoverflow.com/questions/33739623/how-to-add-a-shortcut-key-for-a-jbutton-in-java/33739732#33739732 for an example of this approach. – camickr Mar 17 '21 at 20:45

4 Answers4

1

You can create a list or array of ActionListener's and pass it to the constructor, for example

import java.awt.*;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.*;

public class Loop extends JFrame {

    public Loop(java.util.List<ActionListener> listeners) {

        this.setSize(700, 300);
        this.setLocation(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel jpanel = new JPanel();
        jpanel.setBackground(Color.CYAN);
        jpanel.setLayout(new GridLayout(0, 6));

        JButton[] jButton = new JButton[6];
        String[] string = {"One", "Two", "Three", "Four", "Five", "Six"};

        this.add(jpanel);
        int j = 0;

        for (int i = 0; i < jButton.length; i++) {
            while (j < 6) {
                jButton[i] = new JButton();
                jButton[i].setPreferredSize(new Dimension(50, 50));
                jButton[i].setText(string[j]);
                jpanel.add(jButton[i]);
                jButton[i].addActionListener(listeners.get(j));
                j++;
            }

        }

    }

    public static void main(String[] args) {
        java.util.List<ActionListener> listeners = List.of(
                e -> System.out.println("actionListener 1"),
                e -> System.out.println("actionListener 2"),
                e -> System.out.println("actionListener 3"),
                e -> System.out.println("actionListener 4"),
                e -> System.out.println("actionListener 5"),
                e -> System.out.println("actionListener 6")
        );
        Loop myLoop = new Loop(listeners);
        myLoop.setVisible(true);
    }
}
chptr-one
  • 600
  • 2
  • 15
0

It seems from the code that to add the ActionListener in a for loop gives all the buttons the same action.

You are correct. You are creating an anonymous class that implements ActionListener interface. However, each JButton is assigned a separate instance of that class.

There is nothing wrong with assigning the same, single instance to the different JButtons. Inside the code of the actionPerformed method you can identify which JButton was activated by examining the ActionEvent parameter.

The ActionEvent contains a action command string as well as the source, which, in your case, is the actual JButton that was activated.

Personally, I would make your Loop class implement ActionListener and in the actionPerformed method I would determine which JButton was activated from the action command. The below code demonstrates.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class Loop extends JFrame implements ActionListener {

    public Loop() {
        this.setSize(700, 300);
        this.setLocation(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel jpanel = new JPanel();
        jpanel.setBackground(Color.CYAN);
        jpanel.setLayout(new GridLayout(0, 6));

        JButton[] jButton = new JButton[6];
        String string[] = {"One", "Two", "Three", "Four", "Five", "Six"};

        this.add(jpanel);

        for (int i = 0; i < jButton.length; i++) {
            jButton[i] = new JButton(string[i]);
            jButton[i].setPreferredSize(new Dimension(50, 50));
            jpanel.add(jButton[i]);
            jButton[i].addActionListener(this);
        }
    }

    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();
        switch (actionCommand) {
            case "One":
                break;
            case "Two":
                break;
            case "Three":
                break;
            case "Four":
                break;
            case "Five":
                break;
            case "Six":
                break;
            default:
        }
    }

    public static void main(String[] args) {
        Loop myLoop = new Loop();
        myLoop.setVisible(true);
    }
}

By default, the action command is assigned the text of the JButton but you can assign a different value via the setActionCommand method.

Abra
  • 19,142
  • 7
  • 29
  • 41
  • I don't know, there are actions and then there are `Action` what do they mean by it gives all the buttons the same action. – matt Mar 17 '21 at 20:34
0

You could do something like this:

import java.awt.*;
import java.util.stream.Stream;
import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Loop extends JFrame {
    
        public Loop() {
    
            final JPanel jpanel = new JPanel();
            /**/         jpanel.setBackground(Color.CYAN);
            /**/         jpanel.setLayout(new GridLayout(0, 6));
    
            this.add    (jpanel);
    
            Stream.of("One", "Two", "Three", "Four", "Five", "Six")
            .forEach(text -> {
                final JButton button = new JButton(text);
                /**/          button.setPreferredSize(new Dimension(50, 50));
                /**/          button.addActionListener((e) -> {System.out.println("Button " + text + " pressed");});
                jpanel.add   (button);
            });
    
            this.setPreferredSize(new Dimension(700, 300));
            this.setLocation(400, 300);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            this.pack();
            this.setResizable(false);
            this.setVisible(true);
        }
    
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(() -> new Loop());
        }
    }
Dave The Dane
  • 650
  • 1
  • 7
  • 18
-1

You need some way of identifying which Button, but you can use 1 shared Listener as shown below.

Please note the use of SwingUtilities.invokeLater(...) to execute on the correct Thread.

import java.awt.*;
import java.awt.event.ActionListener;
import java.util.stream.Stream;
import javax.swing.*;

@SuppressWarnings("serial")
public class Loop extends JFrame {

    public Loop() {

        final JPanel jpanel = new JPanel();
        /**/         jpanel.setBackground(Color.CYAN);
        /**/         jpanel.setLayout(new GridLayout(0, 6));

        this.add    (jpanel);

        final ActionListener oneSharedListener = (e) -> System.out.println("Button " + e.getActionCommand() + " pressed");

        Stream.of("One", "Two", "Three", "Four", "Five", "Six")
        .forEach(text -> {
            final JButton button = new JButton(text);
            /**/          button.setPreferredSize(new Dimension(50, 50));
            /**/          button.addActionListener(oneSharedListener);
            /**/          button.setActionCommand(text);
            jpanel.add   (button);
        });

        this.setPreferredSize(new Dimension(700, 300));
        this.setLocation(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.pack();
        this.setResizable(false);
        this.setVisible(true);
    }

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(() -> new Loop());
    }
}
Dave The Dane
  • 650
  • 1
  • 7
  • 18