Skip to content
Advertisement

How to make multiple buttons work individually in Swing?

I am trying to develop an application and I have decided to learn Swing to create the GUI. I have never learnt this before, it seems quite simple but I haven’t gotten my head around ActionListener.

I am trying to make the buttons respond by producing some debug text in the console.

Could someone take a look and point out the obvious.

package GUI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;  
public class MainMenu implements ActionListener{  
JFrame f;
JButton b,b1,b2,b3,b4;

MainMenu(){  
f=new JFrame();//creating instance of JFrame  
          
JButton b=new JButton("click1");//creating instance of JButton  
b.setBounds(130,50,100, 40);
JButton b2=new JButton("click2");//creating instance of JButton  
b2.setBounds(130,150,100, 40);
JButton b3=new JButton("click3");//creating instance of JButton  
b3.setBounds(130,250,100, 40);
JButton b4=new JButton("click4");//creating instance of JButton  
b4.setBounds(130,350,100, 40);
          
f.add(b);//adding button in JFrame  
f.add(b2);
f.add(b3);
f.add(b4);
f.setSize(400,500);//400 width and 500 height  
f.setLayout(null);//using no layout managers  
f.setVisible(true);//making the frame visible  
}  
  
public static void main(String[] args) {  
new MainMenu();  
    }

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == b)
        System.out.println("test 1");
     else if (e.getSource() == b2)
         System.out.println("test 2");
     else if
        (e.getSource() == b3)
         System.out.println("test 3");
     else if
        (e.getSource() == b4)
        System.out.println("test 4");
    
}  
}

Thanks in advance

Advertisement

Answer

In Swing “listeners” are based on a simple concept known as the “observer pattern”. On object registered interest in been notified about by another object when something their inserted in happens.

In your case, when the button is “actioned”.

But, before you solve that problem, you need to fix one other.

You’re shadowing your variables…

public class MainMenu implements ActionListener {

    JFrame f;
    JButton b, b1, b2, b3, b4;

    MainMenu() {
        JButton b = new JButton("click1");//creating instance of JButton  
        JButton b2 = new JButton("click2");//creating instance of JButton  
        JButton b3 = new JButton("click3");//creating instance of JButton  
        JButton b4 = new JButton("click4");//creating instance of JButton  
    }
}

You’ve declared b, b1, b2, b3 and b4 as properties of the MainMenu, but in the constructor have redeclared them as local variables, whose context is only available within the constructor. This means, that if any other method tries to references the properties, they will find them to be null

Now we’ve spotted that problem, we can correct it and, add the magic sauce to make your code work…

b = new JButton("click1");//creating instance of JButton  
b2 = new JButton("click2");//creating instance of JButton  
b3 = new JButton("click3");//creating instance of JButton  
b4 = new JButton("click4");//creating instance of JButton  

b.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);
b4.addActionListener(this);

Here, we’re just saying, “button, tell me when the you’re actioned”

Example…

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.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class MainMenu implements ActionListener {

    JFrame f;
    JButton b, b1, b2, b3, b4;

    MainMenu() {
        f = new JFrame();//creating instance of JFrame  

        b = new JButton("click1");//creating instance of JButton  
        b2 = new JButton("click2");//creating instance of JButton  
        b3 = new JButton("click3");//creating instance of JButton  
        b4 = new JButton("click4");//creating instance of JButton  

        b.addActionListener(this);
        b2.addActionListener(this);
        b3.addActionListener(this);
        b4.addActionListener(this);

        JPanel content = new JPanel(new GridBagLayout());
        content.setBorder(new EmptyBorder(10, 10, 10, 10));
        f.setContentPane(content);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(10, 0, 10, 0);
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        f.add(b, gbc);
        f.add(b2, gbc);
        f.add(b3, gbc);
        f.add(b4, gbc);

        f.pack();
        f.setVisible(true);//making the frame visible  
    }

    public static void main(String[] args) {
        new MainMenu();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == b) {
            System.out.println("test 1");
        } else if (e.getSource() == b2) {
            System.out.println("test 2");
        } else if (e.getSource() == b3) {
            System.out.println("test 3");
        } else if (e.getSource() == b4) {
            System.out.println("test 4");
        }

    }
}

You should also make sure you take the time to read through How to Use Actions and Laying Out Components Within a Container, which is going to save you a lot of hassle in the future.

Expansion …

Like most things, there’s more then one to use a ActionListener.

The “problem” with the approach you’ve started with (and it’s not bad), is that the actionPerformed method is public, so any one can call it. This might not be a bad idea, but it “leaks” implementation detail which might better constrained internally.

Another “issue” is ALL the buttons use the same ActionListener. In generally, that’s not a “bad” thing, but it can quick make your code very complex, difficult to understand and to maintain.

Before addressing those issues, I’ll address the issue of using a object reference to determine what was triggered (as we’ve done above). Again, this is not a “bad” thing, but it begins to limit it’s re-use, as you could have a button, toolbar button and menu button all wanting to do the same thing, wouldn’t it be nice to re-use as much functionality as possible?

The “action command”

A simple way to make a ActionListener more re-usable, is to make use of the actionCommand property. This allows you to specify a String which can be used to identify a given action, but which might be trigged by the user in a number of different ways.

By default, a JButton‘s actionCommand is set to the buttons text, but you can specify your to make it easier.

So, starting with…

b = new JButton("click1");//creating instance of JButton

And then use something like…

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getActionCommand().equals("click1")) {
        System.out.println("test 1");
    } //...
}

to determine when the action is trigged. The point to this is, to decouple the logic. This implementation is no longer reliant on (coupled to) the instance of b! Sweet!

Anonymous classes

Another approach might to make use of “anonymous classes” (spooky). This is a neat feature which allows you to create a inline (someone will point to me that’s wrong term to use 😝) instance of an interface, for example…

b.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("test 1");
    }
});

Now, when the actionPerformed method is called, you are guaranteed who actually triggered it! No need to actionCommand or referencing checking.

See Anonymous Classes for more details.

There are a bunch of other ways you can make this work, but I think I might have scared you enough for one question 😉

Advertisement