问题
Is there a way to continuously update a JLabel inside a loop from a separate thread. Right now the Jlabel gets only updated once the loop is over.
Please do correct me if am wrong, I think this is because the Event Dispatcher Thread is on halt till the other thread finishes executing.
Is there any way that I can run both threads at the same time?
I tried giving a Thread.sleep(300) inside the loop but didn't work.
回答1:
Please do correct me if am wrong, I think this is because the Event Dispatcher Thread is on halt till the other thread finishes executing.
This is wrong. If the other thread is truly running off of the Swing event thread, both should run simultaneously. You've got a bug where either:
- You think that you're running off of the Swing event thread, but you're not or
- You think that you're updating the GUI on the Swing event thread, but you're not.
Bottom line is that you've got a bug in code not shown, and for better answers, show pertinent code.
e.g.,
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
@SuppressWarnings("serial")
public class UpdateLabel extends JPanel {
// label to update
private JLabel statusLabel = new JLabel("");
public UpdateLabel() {
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 3, 3));
topPanel.add(new JLabel("Counter:"));
topPanel.add(statusLabel);
JPanel bottomPanel = new JPanel();
bottomPanel.add(new JButton(new StartThreadAction("Start Thread")));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(bottomPanel, BorderLayout.PAGE_END);
}
// fail safe method that is guaranteed to add text to the label
// **on the Swing event thread**
public void setLabelText(final String text) {
if (SwingUtilities.isEventDispatchThread()) {
statusLabel.setText(text);
} else {
SwingUtilities.invokeLater(() -> {
statusLabel.setText(text);
});
}
}
// Abstract Action for our JButton
private class StartThreadAction extends AbstractAction {
protected static final int MAX_COUNT = 10;
protected static final long SLEEP_TIME = 1;
public StartThreadAction(String name) {
super(name);
}
@Override
public void actionPerformed(ActionEvent e) {
// start a new thread
new Thread(new Runnable() {
private int counter = 0;
@Override
public void run() {
// within the background thread update a counter and sleep
while (counter < MAX_COUNT) {
String text = "" + counter;
setLabelText(text); // call our fail safe method
counter++;
try {
// sleep for 1 second
TimeUnit.SECONDS.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
// rare time it's OK to leave this blank
}
}
}
}).start();
}
}
private static void createAndShowGui() {
UpdateLabel mainPanel = new UpdateLabel();
JFrame frame = new JFrame("UpdateLabel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// start GUI on the Swing event thread
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Having said all this, for something like a simple count with delay, I'd use a Swing Timer because there's no worries about calling code on or off the EDT, since the Timer guarantees that all calls will be on this thread.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.*;
@SuppressWarnings("serial")
public class UpdateLabel2 extends JPanel {
// label to update
private JLabel statusLabel = new JLabel("");
private Timer timer;
public UpdateLabel2() {
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 3, 3));
topPanel.add(new JLabel("Counter:"));
topPanel.add(statusLabel);
JPanel bottomPanel = new JPanel();
bottomPanel.add(new JButton(new StartTimerAction("Start Timer")));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(bottomPanel, BorderLayout.PAGE_END);
}
// Abstract Action for our JButton
private class StartTimerAction extends AbstractAction {
protected static final int MAX_COUNT = 10;
private static final int TIMER_DELAY = 1000; // 1 second
private int counter = 0;
public StartTimerAction(String name) {
super(name);
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
}
@Override
public void actionPerformed(ActionEvent e) {
if (timer != null && timer.isRunning()) {
return; // wait until timer finishes
}
timer = new Timer(TIMER_DELAY, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e2) {
if (counter >= MAX_COUNT) {
timer.stop();
counter = 0;
} else {
counter++;
statusLabel.setText("" + counter);
}
}
});
timer.start();
}
}
private static void createAndShowGui() {
UpdateLabel2 mainPanel = new UpdateLabel2();
JFrame frame = new JFrame("UpdateLabel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// start GUI on the Swing event thread
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
来源:https://stackoverflow.com/questions/41250944/continuously-update-a-jlabel-inside-a-loop-from-separate-thread