在 edt 中更新 swing 组件

Updating swing components within edt

如果我理解正确,那么当我创建 GUI swing 组件时,例如我有这个:

public class frameExample extends JFrame{
    public frameExample(){
    //Here adding bunch if components
    setVisible(true);
    }
}

因此,只要我不调用 setVisible 方法,组件就会从创建实例的线程中生成。因此,如果在 class 中我有我的主要方法,请写:

JFrame test=new frameExample();

我系统输出

Thread.currentThread.getName(); 

在 setVisible 之前的 frameExample 的构造函数中,我应该得到:main.

之后创建和维护 swing 元素的责任被传递给事件调度线程,因为它不是线程安全的,每个组件 add/remove/modify 应该在 EDT 线程中完成。

所以我应该将 setVisible 放在构造函数的最后一行代码中,或者单独调用它。

据我所知,所有事件监听都通过 EDT。因此,如果我在 actionPerformed 方法中创建一个新组件,它应该可以正常工作。

此外,如果我将 运行nable 实例传递给 invokeLater 或 invokeAndWait,那么所有 运行() 方法都将由 EDT 完成。

这就是我感到困惑的原因。

我做了这个代码:

public class GUI extends JFrame {


JButton btn = new JButton("Change");
JMenuBar m = new JMenuBar();

public GUI() {
    super("Test");
    setSize(400, 400);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    m.add(new JMenu("menu"));
    add(m, BorderLayout.NORTH);

    add(btn, BorderLayout.SOUTH);
    System.out.println("Current thread: before setVisible "+Thread.currentThread().getName());

    setVisible(true);

    System.out.println("Current thread: after setVisible "+Thread.currentThread().getName());
    btn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

            add(new JButton("testbtn1"), BorderLayout.EAST);
            add(new JButton("testbtn2"));
            System.out.println("Current thread: "+Thread.currentThread().getName());


            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    for (int i = 0; i < 1E8; i++) {
                        Math.sin(5.0);
                    }
                    System.out.println("Current thread: "+Thread.currentThread().getName());
                }
            }).start();

        }
    });
}

}

所以在我的 anonim class 中,我在 EDT 中添加了两个按钮 im,但是在我调整它的大小后组件没有添加到我的框架中(这会强制 edt 更新它的组件?? ? 这是什么原因?).

所以即使在 edt 中我也没有得到我的新组件,但是当我在 edt 之外创建一个随机线程并让它更改一些 gui 元素时 属性(例如 setText)在 edt 之外工作正常。

所以我的第一个问题是:为什么我的组件没有在 edt 中更新以及为什么它们在调整大小后可见

第二个:为什么我可以在 edt 之外更改 swing 组件并且一切正常?这只是随机线程行为吗,例如,没有同步块,事情就可以正常工作,但是当你重新运行你在某个时候编程时,它最终会因为缺乏同步而崩溃。

So as long as I don't call the setVisible method the components are being made from the thread the instance was created

错了。只要您不专门创建新的 Thread 或使用实用方法(例如 SwingUtilities#invoke... 方法),每次调用都会在当前 Thread 上调用,包括 setVisible打电话。

您似乎认为使 Swing 组件可见会以某种方式使您的代码切换线程。不,Swing 组件的绘制将发生在 EDT 上。正如您正确指出的那样,Swing 不是线程安全的。 这就是为什么您也应该在 EDT 上而不是在另一个线程上创建组件。它可能在大多数时候都没有问题,但最终你会偶然发现奇怪的错误。

As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.

Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.

正确。

So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it

Container 添加组件需要您重新验证布局(请参阅 Container#add 方法的 javadoc)。打电话

revalidate();
repaint();

添加按钮后,它将按预期工作。手动调整框架的大小与您已经注意到的效果相同。

So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.

如前所述,它可能在大多数时间都有效,但不能保证它会在 100% 的时间有效。添加组件未按预期工作的原因已在上面解释,与线程问题无关。