我读到构造Swing组件和处理事件的所有代码必须由Event Dispatch Thread运行。我理解如何使用SwingUtilities.invokeLater()
方法完成此操作。请考虑以下代码,其中GUI初始化在main
方法本身
public class GridBagLayoutTester extends JPanel implements ActionListener {
public GridBagLayoutTester() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("Testing");
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
button.addActionListener(this);
add(button, gbc);
}
public void actionPerformed(ActionEvent e) {
System.out.println("event handler code");
}
public static void main(String[] args) {
JFrame frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
frame.setSize(800, 600);
frame.pack();
frame.setVisible(true);
System.out.println("Exiting");
}
}
这段代码如何完美运行?我们正在构造JFrame
并在主线程中调用许多其他方法。我不明白EDT在这里的确切位置(执行什么代码?)。 GridBagLayoutTester
类的构造函数也是从main
方法调用的,这意味着EDT没有运行它。
简而言之
答案 0 :(得分:12)
代码完美无缺,因为在EDT有机会与之交互之前,您正在主线程中构建框架。从技术上讲,你不应该这样做,但从技术上讲,你可以在这种特殊情况下,因为你不能与JFrame交互,直到它变得可见。
要知道的是,Swing组件不是线程安全的。这意味着它们不能同时从多个线程进行修改。这可以通过确保所有修改都来自EDT来解决。
EDT是一个致力于用户交互的主题。用户生成的任何事件始终在EDT上运行。任何用户界面更新都在EDT上运行。例如,当您调用Component.repaint()
时,可以从任何线程调用此方法。这只是设置一个标志,将组件标记为需要绘画,EDT在下一个循环中完成它。
EDT自动启动,并与系统实施密切相关。它在JVM中处理得很好。通常,它与窗口系统中处理用户交互的单个线程相关。当然,这完全取决于实现。好消息是你不必担心这个。您只需要知道 - 如果您与任何Swing组件进行交互,请在EDT上进行。
同样,还有一件事很重要。如果您要对外部资源进行任何长时间处理或阻止,并且您要响应用户生成的事件而执行此操作,则必须将其安排在EDT自己的线程中运行。如果未能执行此操作,则会在等待长时间处理运行时导致用户界面阻塞。优秀的例子是从文件加载,从数据库读取或与网络交互。您可以使用SwingUtilities.isEventDispatchThread()
方法测试您是否在EDT上(对于创建可以从任何线程调用的中性方法很有用)。
这里有两段代码,我在编写处理EDT的Swing编程时经常使用这些代码片段:
void executeOffEDT() { if (SwingUtilities.isEventDispatchThread()) { Runnable r = new Runnable() { @Override public void run() { OutsideClass.this.executeOffEDTInternal(); } }; new Thread(r).start(); } else { this.executeOffEDTInternal(); } } void executeOnEDT() { if (SwingUtilities.isEventDispatchThread()) { this.executeOnEDTInternal(); } else { Runnable r = new Runnable() { @Override public void run() { OutsideClass.this.executeOnEDTInternal(); } }; SwingUtilities.invokeLater(r); } }
答案 1 :(得分:3)
事件调度线程,顾名思义,每次需要处理事件时都由Swing调用。
在您给出的示例中,当需要处理动作事件时,“测试”按钮将自动调用actionPerformed方法。因此,actionPerformed方法的内容将由Event Dispatch Thread调用。
回答你的两个最后问题:
答案 2 :(得分:1)
1)我不知道是在new JFrame
还是在setVisible
中,但它是按需初始化的,那就是主方法的结束(在主进程线程上)不会终止处理。 EDT已经启动并且在循环中被阻止等待下一个事件。
2)明确地说。该循环从OS接收事件,找到JButton并告诉它该事件被触发。然后该按钮调用侦听器。所有这些都发生在美国东部时间。
您可以查看当您想要终止进程(或关闭主窗口)时调用的Swing代码,以查找EDT终止的位置...这可以为您提供线索(我稍后会这样做! :)
答案 3 :(得分:0)
启动EDT线程后,如果尚未启动,则首先调用setVisible(true);
,ofc。或者,如果您拨打SwingUtilities.invokeAndWait()
或SwingUtilities.invokeLater()
方法。
请参阅http://www.leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html