Search code examples
javavariablesactionlistener

Use value out of ActionListener in Java


I am juste starting to code, and I am trying to write a program were I would need a button to change a value (alea) that would be used out of actionPerformed methode. What I have currently wrote goes like this :

import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ThreadLocalRandom;

import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.*;


public class Quiz {

    public static void main(String[] args) {
        
        
        JButton btn = new JButton("Next");
        btn.setBounds(200, 400, 200, 40);
        
        int alea = 0;

        btn.addActionListener(new ActionListener() {    
            @Override
            public void actionPerformed(ActionEvent e) {
                alea = ThreadLocalRandom.current().nextInt(1, 4 + 1); // I get the error "Local variable name defined in an enclosing scope must be final or effectively final"
                String test = "hello";
            }

        });
         
         String sp = Integer.toString(alea);
         String vue = sp + ".png";
        
         String imgUrl="./images/"+vue;
         ImageIcon imageIcon = new ImageIcon(imgUrl);

....

But like this the alea defined before the button doesn't seem to be the same as the one in the button. I searched the internet for an answer, and I don't understand why it doesn't work... Sorry for my crappy english.

Thanks a lot in advance !

I tried not to declare alea before the button action, but inside the methode, and it did not work. I tried to use a getter and setter, but did not succeed. I searched the internet for solution, I encountered similar problem but the solution of which did not help me.


Solution

  • You're coding this as if it were a linear console program, where this code is called:

    btn.addActionListener(new ActionListener() {    
        @Override
        public void actionPerformed(ActionEvent e) {
            alea = ThreadLocalRandom.current().nextInt(1, 4 + 1);
            String test = "hello";
        }
    });
    

    Before this is called:

    String sp = Integer.toString(alea);
    

    assuming that therefore the value of alea has been updated at this point.

    But that is not how this code works as it is most definitely not a linear program but rather an event-driven GUI program, and so alea won't change until the button's ActionListener changes it. So that is where you should get the new value and do what is needed with it.

    Suggestions:

    • First and foremost, get all that code out of the static main method and into the OOP realm of instance methods and fields.
    • Make the alea variable a private instance (non-static) field, if it is needed in multiple locations in your program.
    • You're programming an event-driven GUI and so you should be listener for events and changing variable state in response to the events. This includes handling changes to the alea variable in the button's ActionListener, as noted above, because that is where you will be notified that it should be changed.
    • Avoid null layouts and setBounds(...) as this will lead to inflexible and hard to maintain GUIs and instead learn and use the Swing layout managers. You can find the layout manager tutorial here: Layout Manager Tutorial, and you can find links to the Swing tutorials and to other Swing resources here: Swing Info.
    • This error, ""Local variable name defined in an enclosing scope must be final or effectively final", will go away if alea is an instance field of the class (not a local variable).

    For example

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.util.concurrent.ThreadLocalRandom;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class QuizFoo extends JPanel {
        public static final int ORIGIN = 1;
        public static final int SPREAD = 4;
        private int alea = 0;
        private JButton nextButton;
        private JTextArea outputArea;;
        
        public QuizFoo() {
            // add a listener to the button and add it to a JPanel
            nextButton = new JButton("Next");
            nextButton.addActionListener(e -> nextActionPerformed(e));
            JPanel buttonPanel = new JPanel();
            buttonPanel.add(nextButton);
            
            // text area to display our GUI output. put it in a scroll pane
            int rows = 30;
            int columns = 50;
            outputArea = new JTextArea(rows, columns);
            outputArea.setFocusable(false);
            JScrollPane scrollPane = new JScrollPane(outputArea);
            
            // make the main jpanel use a borderlayout, and add our components to it
            setLayout(new BorderLayout());
            add(buttonPanel, BorderLayout.PAGE_START);
            add(scrollPane, BorderLayout.CENTER);
        }
        
        // action listener called when button pressed
        private void nextActionPerformed(ActionEvent e) {
            alea = ThreadLocalRandom.current().nextInt(ORIGIN, ORIGIN + SPREAD);
            
            // USE ALEA HERE ****
            
            // for example:
            String textToAppend = "Alea: " + String.valueOf(alea) + "\n";
            outputArea.append(textToAppend);
        }
        
        public static void main(String[] args) {
            // create the GUI in a Swing thread-safe way
            SwingUtilities.invokeLater(() -> {
                QuizFoo mainPanel = new QuizFoo();
    
                JFrame frame = new JFrame("Quiz");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(mainPanel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        }
    }