我目前正在开发一个在MySQL数据库上执行查询的实用程序,我正在使用该接口。
当用户点击“连接”按钮时,状态栏(JTextField)文本应更改为“正在连接...”。这可以正常工作:
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
statusBar.setText("Connecting...");
}
}
});
我实现了一个连接数据库的功能,然后单击“连接”按钮:
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Class.forName("com.mysql.jdbc.Driver");
statusBar.setText("Connecting...");
connection = DriverManager.getConnection("jdbc:mysql://" + database);
}
}
});
在这种情况下,状态栏的文本在建立连接之前不会更改为“正在连接...”。
我删除了一些代码,例如异常处理,以提高可读性。
如何在建立连接之前强制更改状态栏的文本?
答案 0 :(得分:4)
不应在Event Dispatch Thread中执行建立数据库连接。这会阻止您的组件更新。而是在后台线程中执行任务。
如果您需要在执行此操作时报告结果,请使用SwingWorker
类,或使用SwingUtilities
类更新组件,尤其是invokeLater
。这两个都将确保组件在EDT上更新,并且长时间运行的任务在其他地方进行。
有关详情,请参阅Concurrency in Swing。
答案 1 :(得分:3)
正如其他人所提到的,连接逻辑最好在事件调度线程以外的线程上执行。但是,从技术上讲,这不是在建立连接之前不更新文本字段的原因。
发生这种情况的实际原因是内部Swing组件使用数据结构来存储侦听器(在本例中为ActionListener
s),与命令相比,侦听器以反向顺序通知他们被添加了。因此,在您的示例中,在负责更新文本的侦听器之前会通知创建连接的ActionListener
。
一个简单的解决方法是将两个ActionListener
合并为一个代码块;没有理由需要添加多个侦听器。这当然会导致您的GUI在进行连接尝试时被阻止,这就是为什么其他人建议使用诸如SwingWorker
之类的机制来阻止这种情况。
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
statusBar.setText("Connecting...");
new SwingWorker<Void, Void>() {
protected Void doInBackground() {
// Called on a background thread.
connectToDatabase();
return null;
}
protected void done() {
// Called on Event Dispatch thread once connect routine has completed.
try {
get(); // Propagate any exceptions back to Event Dispatch thread.
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null,
"Failed to connect: " + ex.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
}
}
}.execute();
}
});
答案 2 :(得分:2)
每两天就会提出一个问题。
如果在事件调度线程中执行代码,则会阻止此threa,从而阻止它执行文本字段中显示所需的所有重绘操作。
长时间运行的阻塞任务应该在后台线程中运行,并且该线程不能访问Swing组件。使用SwingWorker
。它的javadoc解释了一切。它还有一个指向Swing教程相关部分的链接,你应该阅读。
答案 3 :(得分:1)
这是因为您在EDT(AWT事件调度线程)中建立连接。在进行连接时,不再需要更新,处理用户输入和重新绘制屏幕上的窗口(以图形方式)。这意味着整个应用程序似乎都被冻结,直到建立连接为止 所以要解决这个问题,你必须在另一个线程中建立连接。另一种肮脏的,不推荐的方法是在更改文本后强制EDT重新绘制屏幕。这是最简单的工作方式,但不是整齐的方式。
这可以通过调用repaint();
然后调用update(getGraphics());
来完成。但是非常脏。我认为你的屏幕会闪烁。但这很好地证明了这个问题。首先测试它可能会很有趣,看看究竟发生了什么。