简体   繁体   中英

ActionListener and for loop

Dear friends of Stackoverflow,

here what I'm trying to do: - I would like to have a simple frame with some buttons and a JTextArea - I would like to have a loop that, at every iteration, expects for me to click on a button: when I click on this button a bunch of stuff happens, but I can't get it right: - in one attempt I got the for loop to work but it would not stop, at every turn, it just took the first command and performed all 20 turns without stopping - in the current version I click the button and nothing happens - I have already researched SOF and a series of other sites, including the Oracle documents but (probably also due to my level of experience), I cannot find an explanation that is clear enough for me to understand

here my code

package game4_prova_forloop;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class GAME4_prova_forloop {

    public static void main(String[] args) {

        //create frame
        JFrame frame = new JFrame("Action Listener");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 600);
        frame.setLayout(new FlowLayout());
        frame.setVisible(true);   

        //crete text area panel, 
        JPanel pannelloTextArea = new JPanel(); 
        pannelloTextArea.setBackground(new Color(255, 204, 204));
        pannelloTextArea.setSize(400, 400);
        frame.add(pannelloTextArea); 
        GroupLayout pannelloTextAreaLayout = new GroupLayout(pannelloTextArea); 

        //create scrollPane
        JScrollPane scrollTextArea = new JScrollPane(); 

        //1) create JTextArea
        JTextArea mostraTesto = new JTextArea(20, 20); 
        mostraTesto.setLineWrap(true);  //make it wrap text 
            pannelloTextArea.add(scrollTextArea); //add it to scroll pane
            pannelloTextArea.revalidate();
                scrollTextArea.add(mostraTesto); //add the scroll pane to text area 
                scrollTextArea.setViewportView(mostraTesto); 

        //2) create buttons 
        JButton action1 = new JButton("1");
        frame.add(action1);
        JButton action2 = new JButton("2");
        frame.add(action2);
        JButton action3 = new JButton("3");
        frame.add(action3);

        //3) pass textArea in the RoundLevelAction class
        RoundLevelAction roundLevelActionObj = new RoundLevelAction(mostraTesto); //first issue: I get an error
        //4) add listener to JButtons
        action1.addActionListener(roundLevelActionObj);
        action2.addActionListener(roundLevelActionObj);
        action3.addActionListener(roundLevelActionObj);          
    }

//THIS IS WHERE I START TO HAVE PROBLEMS: WHEN I CLICK NOTHING HAPPENS, WHEN 
//I WOULD EXPECT THE FOR LOOP TO GO THROUGH ITERATIONS
    public class RoundLevelAction implements ActionListener {

        //add inside the listener the pieces of GUI that you'll use
        private JTextArea mostraTesto;
        private Object action1;
        private Object action2;
        private Object action3;
        private Object action4;
        private Object action5;
        private Object action6;

        //create class for JTextArea
        public RoundLevelAction(JTextArea mostraTesto){
            this.mostraTesto = mostraTesto; 
        }    

        //and, finally, what I really want to do: a loop that, at each turn, expects me to click on 
        //a button and does an action in response
        public void actionPerformed(ActionEvent e) {
            //now create the loop
            for (int round_counter=1; round_counter<21; round_counter++) {                                       
                if (e.getSource()==action1){                             
                    mostraTesto.append("\n description action 1 and a bunch of other stuff");
                }
                else if (e.getSource()== action2){ 
                    mostraTesto.append("\n description action 2 and a bunch of other stuff");
                } 
                else if (e.getSource()== action3){
                    mostraTesto.append("\n description action 3 and a bunch of other stuff");
                } 
            }
        }
    }

}

IMPORTANT: I'm well aware that the code above does not fit Java best practices: it's just a sample code to illustrate what I'm trying to do (the original code is a lot of lines in multiple classes)

I hope you can help me to understand where I'm doing wrong

Many thanks in advance

Why is your code currently not working, and what was probably wrong with your previous attempt.

action1.addActionListener(roundLevelActionObj);
action2.addActionListener(roundLevelActionObj);
action3.addActionListener(roundLevelActionObj);  

This will add the same listener to each of your buttons. When you click on either of these buttons, an ActionEvent is generated and send to the ActionListener .

When you have a for loop in your ActionListener , the whole loop will be executed each time you click on any of these buttons. It is the whole code block in the actionPerformed method that gets executed. This is probably what you discovered in your first attempt.

Your current attempt has if (e.getSource()==action1) checks in the for loop. However, the action1 in that statement is not the same as the button on which you clicked. The action1 here refers to the field

private Object action1;

in your RoundLevelAction class. If you use a debugger, you will see that none of those if statements evaluate to true , which is why you have the impression that nothing happens. In fact, the for loop is triggered but nothing is outputted because none of the if statements evaluated to true.

All the above can easily be discovered if you use a debugger, and place some breakpoints.

Now for the solution to your problem. That is unclear to me.

I would like to have a loop that, at every iteration, expects for me to click on a button

The problem with this requirement is that Swing is single threaded. This means that all Swing related operations should happen on a single thread (the E(vent)D(ispatch(T(hread)), and that same thread is used to handle the user input (eg mouse clicks) and to paint the UI. This also means that if you somehow block that single thread, your UI becomes unresponsible.

So if you have a loop, you cannot simply block the EDT and wait for a button click. Since the EDT is blocked, it becomes impossible to click on the EDT.

  • Either you loop on a separate thread, and wait in that thread until the user clicked on the button. The ActionListener associated to that button can then re-activate the thread
  • You can show a dialog asking the user what to do next, and put those buttons in that dialog. Take a look at the JOptionPane#show... methods for this.

the excellent answer from Robin, although not a direct solution, made me think about the logic of the program, and ask myself: why do I have a for loop at all? To count the turns backwards from 20 to 0? Well..I can always count forward (duh!). So I got rid of the for loop altogether, put a counter++for each one of the else ifs, and encapsulated everything into an if counter<20. Now every iteration is activated by the pressure of one of the buttons, the iterations go up and the game follows the logic. NOT THE BEST SOLUTION but "a" solution. I hope this can be useful for another user

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM