Why is my flow not working as expected in Swing?

I print a simple value to add JTextArea

with a simple for loop, and when I run it, it fires correctly if I print the value in the console output ...

But if I add JTextArea

and print the value in the text area, they are added after the whole program finishes.

public class SwingThread {

private JFrame frame;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                SwingThread window = new SwingThread();
                window.frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the application.
 */
public SwingThread() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JScrollPane scrollPane = new JScrollPane();
    frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

    JTextArea textArea = new JTextArea();
    scrollPane.setViewportView(textArea);

    JButton btnNewButton = new JButton("New button");
    scrollPane.setColumnHeaderView(btnNewButton);
    btnNewButton.addActionListener(new ActionListener()
    {
        public void actionPerformed(ActionEvent arg0)
        {
            try
            {
                for(int i = 0 ; i <= 5 ; i++)
                {
                    textArea.append("Value "+i+"\n");
                    System.out.println("Value is" + i);
                    Thread.sleep(1000);
                }
            }
            catch(Exception e)
            {
                System.out.println("Error : "+e);
            }
        }
    });
}
}

      

I want to add one by one, but it was added after running the whole program.

+3


source to share


1 answer


Your problem is usage related Thread.sleep

, since when you call this in the Swing event flow (or EDT for the event flow), as you do, it puts the entire Swing event flow to sleep. When this happens, the actions of this thread cannot be performed, including painting the GUI (refresh) and user interaction, and this will freeze your GUI completely - this is not good. In this situation, the solution is to use the Swing Timer as a pseudo loop. The timer creates a loop on a background thread and ensures that all code in its actionPerformed method is called on the Swing event thread, which is necessary here because we don't want to add this thread to the JTextArea.

Also, as others have pointed out, if all you want to do is do a re-action with a delay in Swing, then yes, use this Swing timer. If, on the other hand, you want to run a long bit of code in Swing, then again that code will block the EDT and freeze your program. For this situation, use a background thread such as the one supplied by SwingWorker. Please see Tutorial: Concurrency in Swing to learn more about this.

eg.

btnNewButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
        // delay between timer ticks: 1000
        int timerDelay = 1000;
        new Timer(timerDelay, new ActionListener() {
            private int counter = 0;
            @Override
            public void actionPerformed(ActionEvent e) {
                // timer stopping condition
                if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
                    ((Timer) e.getSource()).stop();
                } else {
                    textArea.append("Value " + counter + "\n");
                }
                counter++; // increment timer counter variable
            }
        }).start();
    }
});

      



All:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;

import javax.swing.*;

public class SwingThread2 {
    protected static final int MAX_VALUE = 5; // our constant
    private JFrame frame;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SwingThread2 window = new SwingThread2();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SwingThread2() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        // frame.setBounds(100, 100, 450, 300); // avoid this
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JScrollPane scrollPane = new JScrollPane();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        JTextArea textArea = new JTextArea(15, 40);
        scrollPane.setViewportView(textArea);

        JButton btnNewButton = new JButton("New button");
        scrollPane.setColumnHeaderView(btnNewButton);
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                // delay between timer ticks: 1000
                int timerDelay = 1000;
                new Timer(timerDelay, new ActionListener() {
                    private int counter = 0;
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // timer stopping condition
                        if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
                            ((Timer) e.getSource()).stop();
                        } else {
                            textArea.append("Value " + counter + "\n");
                        }
                        counter++; // increment timer counter variable
                    }
                }).start();
            }
        });

        // better to avoid setting sizes but instead to
        // let the components size themselves vis pack
        frame.pack();
        frame.setLocationRelativeTo(null);
    }
}

      

Just for more information, here is an example of the same program that uses the SwingWorker to take a long action and then updates the JProgressBar with that action. The worker is pretty simple and just uses a while loop to advance a counter variable with a limited random amount. It then passes that value in to update its own progress property (a value that can only be between 0 and 100 and so in other situations the value must be normalized to match this). I attach a PropertyChangeListener to a worker and this is notified on the Swing event threadwhen the value of the workflow changes, and also whenever the SwingWorker changes state, such as when it is running. In the latter case, the working StateValue becomes StateValue.DONE. The listener then updates the GUI. Please ask if you have any questions.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

public class SwingThread2 {
    protected static final int MAX_VALUE = 5; // our constant
    private JFrame frame;
    private JProgressBar progressBar = new JProgressBar();

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SwingThread2 window = new SwingThread2();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SwingThread2() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        // frame.setBounds(100, 100, 450, 300); // avoid this
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JScrollPane scrollPane = new JScrollPane();
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        JTextArea textArea = new JTextArea(15, 40);
        scrollPane.setViewportView(textArea);

        JButton btnNewButton = new JButton("New button");
        scrollPane.setColumnHeaderView(btnNewButton);
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                // delay between timer ticks: 1000
                int timerDelay = 1000;
                new Timer(timerDelay, new ActionListener() {
                    private int counter = 0;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        // timer stopping condition
                        if (counter >= MAX_VALUE) { // MAX_VALUE is a constant
                                                    // int = 5
                            ((Timer) e.getSource()).stop();
                        } else {
                            textArea.append("Value " + counter + "\n");
                        }
                        counter++; // increment timer counter variable
                    }
                }).start();
            }
        });

        progressBar.setStringPainted(true);
        JPanel bottomPanel = new JPanel();
        bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
        bottomPanel.add(new JButton(new MyAction("Press Me")));
        bottomPanel.add(progressBar);

        frame.getContentPane().add(bottomPanel, BorderLayout.PAGE_END);

        // better to avoid setting sizes but instead to
        // let the components size themselves vis pack
        frame.pack();
        frame.setLocationRelativeTo(null);
    }

    private class MyAction extends AbstractAction {
        public MyAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public void actionPerformed(ActionEvent e) {
            progressBar.setValue(0);
            setEnabled(false);
            MyWorker myWorker = new MyWorker();
            myWorker.addPropertyChangeListener(new WorkerListener(this));
            myWorker.execute();
        }
    }

    private class WorkerListener implements PropertyChangeListener {
        private Action action;

        public WorkerListener(Action myAction) {
            this.action = myAction;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
                int progress = (int) evt.getNewValue();
                progressBar.setValue(progress);
            } else if ("state".equals(evt.getPropertyName())) {
                if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                    action.setEnabled(true);

                    @SuppressWarnings("rawtypes")
                    SwingWorker worker = (SwingWorker) evt.getSource();
                    try {
                        // always want to call get to trap and act on 
                        // any exceptions that the worker might cause
                        // do this even though get returns nothing
                        worker.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private class MyWorker extends SwingWorker<Void, Void> {
        private static final int MULTIPLIER = 80;
        private int counter = 0;
        private Random random = new Random();

        @Override
        protected Void doInBackground() throws Exception {
            while (counter < 100) {
                int increment = random.nextInt(10);
                Thread.sleep(increment * MULTIPLIER);
                counter += increment;
                counter = Math.min(counter, 100);
                setProgress(counter);
            }
            return null;
        }
    }
}

      

+5


source







All Articles