Swing GUI 下的长进程:意外延迟
Long process under Swing GUI : unexpected delay
在这里解释我的问题是一个 MCVE,点击 JButton
on JDialog
A 打开 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);
}
}
正如您在代码中看到的,我在创建 DiagB
后添加了 3 秒的延迟。
单击按钮 DiagB
显示如下:
仅在 3 秒延迟结束后,DiagB
完整显示:
我的问题是:
一个。为什么DiagB
建好后显示不全? (仅当 showDiag()
returns 时才完整显示)。
b。我提出问题的原因是 DiagB
需要通过 DiagA
中的长进程进行更新。
正确的更新方式是什么?是否需要为每个更新过程使用 SwingWorker
?
一个。 showDiag
在 GUI 线程上运行。当您让 GUI 线程休眠时,GUI 将完全停止。
b。是的,对 运行 长任务使用 SwingWorker
,并使用 SwingUtilities.invokeLater()
将 GUI 更新任务提交回 GUI 线程。或者,实现 SwingWorker#done()
,这是一种在 SwingWorker
任务完成后在 GUI 线程上运行的便捷方法。
根据 Marko Topolnik 的回答和 Andrew Thompson 的评论,我用 SwingWorker
扭曲了这个漫长的过程。
这很好用:
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));
}
}
在这里解释我的问题是一个 MCVE,点击 JButton
on JDialog
A 打开 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);
}
}
正如您在代码中看到的,我在创建 DiagB
后添加了 3 秒的延迟。
单击按钮 DiagB
显示如下:
仅在 3 秒延迟结束后,DiagB
完整显示:
我的问题是:
一个。为什么DiagB
建好后显示不全? (仅当 showDiag()
returns 时才完整显示)。
b。我提出问题的原因是 DiagB
需要通过 DiagA
中的长进程进行更新。
正确的更新方式是什么?是否需要为每个更新过程使用 SwingWorker
?
一个。 showDiag
在 GUI 线程上运行。当您让 GUI 线程休眠时,GUI 将完全停止。
b。是的,对 运行 长任务使用 SwingWorker
,并使用 SwingUtilities.invokeLater()
将 GUI 更新任务提交回 GUI 线程。或者,实现 SwingWorker#done()
,这是一种在 SwingWorker
任务完成后在 GUI 线程上运行的便捷方法。
根据 Marko Topolnik 的回答和 Andrew Thompson 的评论,我用 SwingWorker
扭曲了这个漫长的过程。
这很好用:
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));
}
}