Reputation: 87
When I implement ActionListener as an inner class of the registered listener class then it works fine but when I try to do that outside of the registered class, i.e., outer class, then the button action does not work.
Can anyone explain me why? Or any suggestion to implement ActionListener as an outer class?
The code is below. Thanks in advance.
// MainMethod.java
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainMethod
{
public static void main(String[] args)
{
Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
JFrame frame = new Framework("Tic Tac Toe", size.width / 4,
size.height / 3, 950, size.height / 8);
JPanel panel = new ButtonPanel();
frame.getContentPane().add(panel);
frame.setVisible(true);
}
}
// ButtonPanel.java
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
public class ButtonPanel extends JPanel
{
private static final long serialVersionUID = -4829885180501818987L;
JButton buttons[] = new JButton[9];
ButtonPanel()
{
String name[] = { "button1", "button2", "button3", "button4", "button5",
"button6", "button7", "button8", "button9" };
Border lineBorder = new LineBorder(Color.black, 2);
GridLayout panelLayout = new GridLayout(3, 3);
setLayout(panelLayout);
for (int i = 0; i < name.length; i++)
{
buttons[i] = new JButton(name[i]);
buttons[i].setBackground(Color.white);
buttons[i].setBorder(lineBorder);
buttons[i].addActionListener(new ButtonActionListener());
add(buttons[i]);
}
}
// Inner class implementation
class ButtonActionListener implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
if (ae.getActionCommand().equals("button1"))
{
buttons[0].setText("X");
}
}
}
}
And following is the outer class implementation which doesn't work when used:
// ButtonActionListener.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class ButtonActionListener implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
ButtonPanel buttonPanel = new ButtonPanel();
if (ae.getActionCommand() == "button1")
{
buttonPanel.buttons[0].setText("X");
}
}
}
Upvotes: 1
Views: 3455
Reputation: 3023
In your outer ButtonActionListener
class, write something like the following:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class ButtonActionListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (event.getActionCommand().equals("button1"))
{
((JButton) (event.getSource())).setText("X");
}
}
}
You can call the getSource()
method to fetch the component which called this actionPerformed(ActionEvent)
method, and then cast it to JButton
, since we know its a ButtonActionListener
, and then set its appropriate text.
UPDATE:
Here's a demonstration of how to use outer ActionListener class:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Demo
{
public static final String BUTTON_1 = "Demo.BUTTON_1";
public static final String BUTTON_2 = "Demo.BUTTON_2";
public Demo()
{
JFrame frame = new JFrame("Outer ActionListener");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout(FlowLayout.CENTER));
ButtonActionListener buttonActionListener = new ButtonActionListener();
JButton button1 = new JButton("Button 1");
button1.setActionCommand(Demo.BUTTON_1);
button1.addActionListener(buttonActionListener);
JButton button2 = new JButton("Button 2");
button2.setActionCommand(Demo.BUTTON_2);
button2.addActionListener(buttonActionListener);
frame.add(button1);
frame.add(button2);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args)
{
// Doing this because Swing is not thread safe.
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
new Demo();
}
});
}
}
class ButtonActionListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent event)
{
String actionCommand = event.getActionCommand();
// Since Java 7, String can be used in switch statements
switch (actionCommand)
{
case Demo.BUTTON_1:
System.out.println("Button 1 is clicked.");
break;
case Demo.BUTTON_2:
System.out.println("Button 2 is clicked.");
break;
default:
System.out.println("I don't know!");
break;
}
}
}
Hope your doubts are clear by now.
By the way, you can also use Anonymous Inner classes for adding listeners on components, and since JDK 8, Lambda Expressions. You can search how those things are done online (if you're interested to know other ways of implementing the same thing).
Upvotes: 4
Reputation: 285403
You're creating a new ButtonPanel object inside of your outer ActionListener. This new ButtonPanel object is completely distinct from the one that is being displayed and interacting with the user.
A solution: pass in a reference to the true ButtonPanel instance into your outer listener via the listener's constructor.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class ButtonActionListener implements ActionListener
{
private ButtonPanel buttonPanel;
public ButtonActionListener(ButtonPanel buttonPanel)
{
this.buttonPanel = buttonPanel;
}
public void actionPerformed(ActionEvent actionEvent)
{
// ButtonPanel bp=new ButtonPanel();
if (actionEvent.getActionCommand().equals("button1"))
{
buttonPanel.buttons[0].setText("X");
}
}
}
Then you can set this up via:
buttons[i].addActionListener(
new ButtonActionListener(/*pass in the valid ButtonPanel reference here*/));
Another and better option is to link the buttons to a Model object, give your control a reference to the Model object and have it make method calls on the model or notification calls on its model listeners.
Upvotes: 2