Rinku Chowdhury
Rinku Chowdhury

Reputation: 87

How to implement ActionListener using an outer class?

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

Answers (2)

Aman Agnihotri
Aman Agnihotri

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

Hovercraft Full Of Eels
Hovercraft Full Of Eels

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

Related Questions