Java Swing GUI下的长进程:意外延迟

Java Swing GUI下的长进程:意外延迟,java,swing,swingworker,Java,Swing,Swingworker,为了解释我的问题,这里有一个MCVE,单击JDialoga上的JButton打开JDialogB: 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.swin

为了解释我的问题,这里有一个MCVE,单击
JDialog
a上的
JButton
打开
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
才会完整显示:

我的问题是:
a。为什么构建后,
DiagB
不完全显示?(仅当
showDiag()
返回时,它才会完整显示)。
b。我的问题的原因是,
DiagB
需要通过
DiagA
中的长过程进行更新。

更新的正确方式是什么?是否需要为每个更新过程使用
SwingWorker

a<代码>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));
    }
}
导入java.awt.BorderLayout;
导入java.awt.Color;
导入java.awt.FlowLayout;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入javax.swing.JButton;
导入javax.swing.JDialog;
导入javax.swing.JFrame;
导入javax.swing.JLabel;
导入javax.swing.SwingWorker;
公共类DiagA扩展JDialog{
私有框架b框架;
专用按钮btn;
公共诊断(){
超级();
设置标题(“主框架”);
设置大小(200150);
设置位置(400400);
btn=新的JButton(“显示帧B”);
btn.addActionListener(新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件arg0){
show2ndFrame();
}
});
添加(btn,BorderLayout.NORTH);
setVisible(真);
}
void show2ndFrame(){
if(frame==null){
frame=新FrameB();
btn.setText(“退出”);
}否则{
系统出口(0);
}
销钉();
}
私房{
SwingWorker sw=新SwingWorker(){
@凌驾
受保护的Void doInBackground()引发异常{
试一试{

对于(int i=1;ii)来说,如果它是为了显示而不是为了用户交互,那么它不应该是一个用于与用户对话的
JDialog
。而且在事件处理程序中永远不应该有睡眠(或任何其他长操作)。@realponsign我理解你对事件处理程序中长操作的看法。(
JDialog
JFrame
在这种情况下没有多大区别。例如,可能需要使用
JDialog
).Modality意味着需要立即进行用户交互。否则,就无法正确使用。问得好!是的,
SwingWorker
是解决此问题的方法。@andrewhompson感谢您的反馈。我很感激。谢谢。我明白您所说的“以后安排行动”。在每个更新过程中使用
SwingWorker
是一件非常麻烦的事情。我宁愿在更新的(
DiagB
)中实现它对象。有什么建议吗?@MarkoTopolnik注意到OP使用睡眠来模拟一个长时间运行的任务,所以他们在帖子底部附近建议一个
SwingWorker
,似乎是最好的策略。@AndrewThompson True,我更新了建议
SwingWorker
+
invokeLater