1

Consider the following code, used to change the color of a button when it is clicked.

import java.awt.Color;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;

public class ChangeColor {

    public static void main(String[] args) {

        JButton button = new JButton();
        button.setBackground(Color.BLUE);

        button.addActionListener(new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                button.setBackground(Color.RED);

            }
        });

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(button);
        frame.setSize(200, 200);
        frame.setVisible(true);

    }
}

It doesn't work, because of this error:

Cannot refer to the non-final local variable defined in an enclosing scope

How can I do this without mark the button as final?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
ᴜsᴇʀ
  • 1,109
  • 2
  • 9
  • 23

2 Answers2

3

One solution would be to get the JButton's reference from the ActionEvent parameter:

        @Override
        public void actionPerformed(ActionEvent e) {
            // the source here refers to the object that provoked this method to occur
            JButton sourceBtn = (JButton) e.getSource();
            sourceBtn.setBackground(Color.RED);
        }

Also you could make button a field, not a local variable.
Also you could declare it as final.

Note that you'd never use code such as what you're posting since most non-trivial GUI programs would not be created inside of a static main method as you're doing, but rather would be done within OOP-compliant GUI classes.

For example:

import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class ButtonColorChange extends JPanel {
    private JButton button = new JButton(new ButtonAction("Press Me"));

    public ButtonColorChange() {
        add(button);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("ButtonColorChange");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new ButtonColorChange());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }
}

class ButtonAction extends AbstractAction {
    private static final Color COLOR_1 = Color.BLUE;
    private static final Color COLOR_2 = Color.RED;

    public ButtonAction(String name) {
        super(name);
        int mnemonic = (int) name.charAt(0);
        putValue(MNEMONIC_KEY, mnemonic);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        AbstractButton btn = (AbstractButton) e.getSource();
        Color c = btn.getBackground();
        c = c == COLOR_1 ? COLOR_2 : COLOR_1;
        btn.setBackground(c);
    }
}

Edit regarding further questions of yours:

Actually the code in my question it's only an example, the real code is something like your.

Then better to show the real code, or best a minimal compilable runnable example that simulates the real code.

(a) It is preferred to use e.getSource() or to make button a field?

Either or. If the same Action is to be used on multiple buttons, and you are only interested in the button pressed, then use getSource(). If your Action or ActionListener is an inner class, then a field would work fine. If not, then you could pass the variable into the Action via a constructor parameter as per another answer to your question.

(b) You say that Also you could declare it as final but I receive this error: The local variable may not have been initialized. What I'm wrong?

Without your real code, it's hard to tell, but likely you don't initialize the button at the same time that you declare it.

(c) I my GUI program I don't launch the frame from a thread (id est I write the code of createAndShowGui() in the main(), without the thread). What is the difference?

My code guarantees that my GUI will start up on the Swing event thread, something that should be done for thread safety.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 1
    Thank you, very comprehensive response. Actually the code in my question it's only an example, the real code is something like your. I have a few more questions: **(a)** It is preferred to use `e.getSource()` or to make button a field? **(b)** You say that *Also you could declare it as final* but I receive this error: `The local variable may not have been initialized`. What I'm wrong? **(c)** I my GUI program I don't launch the frame from a thread (id est I write the code of `createAndShowGui()` in the `main()`, without the thread). What is the difference? – ᴜsᴇʀ Aug 31 '15 at 16:52
0

You can give the Button as a constructor parameter to the listener.

class ButtonColorAction extends AbstractAction {
  private JButton button;
  public ButtonColorAction(JButton button) {
    this.button = button;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    button.setBackground(Color.RED);
  }
}

And use it like:

button.addActionListener(new ButtonColorAction(button));
Christian
  • 3,551
  • 1
  • 28
  • 24