问题
To explain my question here is an MCVE where clicking clicking a JButton on JDialog A opens JDialog B:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
public class DiagA extends JDialog {
private DiagB diag;
public DiagA() {
super();
setTitle("main diag");
setSize(200, 150);
setLocation(400,400);
JButton btn = new JButton("Show DiagB");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
showDiag();
}
});
add(btn, BorderLayout.NORTH);
//make main frame visible
setVisible(true);
}
void showDiag() {
if(diag == null) {
diag = new DiagB();
//this prints out as expected
System.out.println("set visible done");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {}
//only after the delay diag shows in full
}
}
public static void main(String[] args) {
new DiagA();
}
}
class DiagB extends JDialog {
public DiagB() {
super();
setTitle("2nd diag");
setSize(150, 100);
setLocation(600,420);
setLayout(new FlowLayout(FlowLayout.CENTER));
getContentPane().setBackground(Color.YELLOW);
setVisible(true);
}
}
As you can see in the code I added a 3 sec delay after creating DiagB.
Clicking the button DiagBshows like this:
Only after the 3 sec delay ends, DiagBshows in full:
My questions are:
a. Why doesn't DiagBshow completely after it is constructed ? (It shows in full only when showDiag() returns).
b. The reason for my question is that DiagB needs to be updated, by long processes in DiagA.
What is the right way to update ? Does it require using a SwingWorker for every updating process ?
回答1:
a. showDiag runs on the GUI thread. The GUI will be completely dead while you make the GUI thread sleep.
b. Yes, use a SwingWorker for long-running tasks and use SwingUtilities.invokeLater() to submit GUI-updating tasks back to the GUI thread. Alternatively, implement SwingWorker#done() which is a convenience method that runs on the GUI thread after the SwingWorker task completes.
回答2:
Based on Marko Topolnik answer and Andrew Thompson comment, I warped the long process with a SwingWorker.
This works fine:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
public class DiagA extends JDialog {
private FrameB frame;
private JButton btn;
public DiagA() {
super();
setTitle("main frame");
setSize(200, 150);
setLocation(400,400);
btn = new JButton("Show Frame B");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
show2ndFrame();
}
});
add(btn, BorderLayout.NORTH);
setVisible(true);
}
void show2ndFrame() {
if(frame == null) {
frame = new FrameB();
btn.setText("Exit");
}else {
System.exit(0);
}
doWork();
}
private void doWork() {
SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
try {
for(int i = 1 ; i<=100 ; i++) {
//represents a long process
Thread.sleep(100);
frame.update(i);
}
} catch (InterruptedException ex) {}
return null;
}
};
sw.execute();
}
public static void main(String[] args) {
new DiagA();
}
}
class FrameB extends JFrame {
JLabel label;
public FrameB() {
super();
setTitle("2nd frame");
setSize(150, 100);
setLocation(600,420);
setLayout(new FlowLayout(FlowLayout.CENTER));
getContentPane().setBackground(Color.YELLOW);
label = new JLabel("0");
add(label);
setVisible(true);
}
void update(int progress) {
label.setText(String.valueOf(progress));
}
}
来源:https://stackoverflow.com/questions/39484450/long-process-under-swing-gui-unexpected-delay