Java Swing:在长事件期间重绘组件
我正在使用 Java Swing 来呈现应用程序的 GUI。我在 JButton 上设置了一个 ActionListener 来启动一系列测试。理想情况下,我想使用实时测试信息和结果更新状态区域(当前为 JTextArea)。换句话说,程序会在测试运行和完成时逐行设置文本区域,如下所示:
Running Tests... Test 1: Check Something... Success Test 2: Check Something else... Success .. //More testing output All tests have completed successfully. Yay.
问题是,当我的 actionPerformed
时,我无法更新状态文本事件方法仍在运行。当所有测试都快速完成时,这一切都很好。然而,我的一些测试需要无限的时间才能完成(想想“数据库连接超时”)。发生这种情况时,用户会陷入状态区域中已有的任何文本,并且我的应用程序会冻结,直到测试完成或失败。
每次更新状态时,我都尝试在 JTextArea
上调用 repaint
,但似乎只是将其添加到事件队列中(直到之后才会被触发)我的 actionPerformed
事件完成...doh)。我还尝试过调用 revalidate
和 updateUI
,甚至尝试在运行的 ActionListener
类上调用 wait()
我的测试,但到目前为止没有任何效果。这是我当前代码的结构:
..
JTextArea statusOutput_textArea;
..
public void actionPerformed(ActionEvent e) {
setStatusText("Running Tests...");
//Run Tests
int currentTest = 1;
//Check something
appendStatusText(" Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
appendStatusText(" Success");
//Check something else
appendStatusText(" Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
appendStatusText(" Success");
//Other tests
.. //Run other tests here
appendStatusText("\nAll tests have completed successfully. Yay.");
}//End of actionPerformed method
public void setStatusText (String statusText) {
statusOutput_textArea.setText(statusText);
statusOutput_textArea.repaint();
}//End of setStatusText method
public void appendStatusText (String statusTextToAppend) {
statusOutput_textArea.setText(statusOutput_textArea.getText() + "\n" + statusTextToAppend);
statusOutput_textArea.repaint();
}//End of appendStatusText method
任何帮助将不胜感激:)
更新
对于那些对解决方案的一般结构感兴趣的人,这里是:
public class RunTestsButtonActionListener implements ActionListener {
JTextArea statusOutput_textArea;
JButton testDatabaseSettings_JButton;
public RunTestsButtonActionListener(JTextArea statusOutput_textArea){
this.statusOutput_textArea = statusOutput_textArea;
}
public void actionPerformed(ActionEvent e) {
testDatabaseSettings_JButton = (JButton) e.getSource();
Thread tests = new Thread(){
public void run() {
//Disable button and add tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("Running Tests...");
//Run Tests
try {
statusOutput_textArea.setText("Running Tests...");
int currentTest = 1;
//Check something
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
statusOutput_textArea.append("\n Success");
//Check something else
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
statusOutput_textArea.append("\n Success");
//Other tests
.. //Run other tests here
statusOutput_textArea.append("\n\nAll tests have completed successfully. Yay.");
} finally {
//Enable button and remove tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("");
}
}
};
tests.start();
}
}
I am working with Java Swing to render my GUI for an application. I have an ActionListener set up on a JButton
to fire off a series of tests. Ideally, I would like to update a status area (currently a JTextArea
) with real-time testing information and results. In other words, the program would set the text area line-by-line as tests are run and completed to something like:
Running Tests... Test 1: Check Something... Success Test 2: Check Something else... Success .. //More testing output All tests have completed successfully. Yay.
The problem is that I can't get the status text to update while my actionPerformed
event method is still running. This is is all fine and dandy when all of the tests complete quickly. However, some of my tests take an indefinite amount of time to complete (think "database connection timeout"). When this happens, users are stuck with whatever text was already in the status area and my application freezes until the tests complete or fail.
I've tried calling repaint
on my JTextArea
every time I update the status, but it seems that just adds it to the event queue (which won't get fired till after my actionPerformed
event completes...doh). I've also tried calling revalidate
and updateUI
, and even tried calling wait()
on the ActionListener
class running my tests, but nothing has worked thus far. Here is the structure of my current code:
..
JTextArea statusOutput_textArea;
..
public void actionPerformed(ActionEvent e) {
setStatusText("Running Tests...");
//Run Tests
int currentTest = 1;
//Check something
appendStatusText(" Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
appendStatusText(" Success");
//Check something else
appendStatusText(" Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
appendStatusText(" Success");
//Other tests
.. //Run other tests here
appendStatusText("\nAll tests have completed successfully. Yay.");
}//End of actionPerformed method
public void setStatusText (String statusText) {
statusOutput_textArea.setText(statusText);
statusOutput_textArea.repaint();
}//End of setStatusText method
public void appendStatusText (String statusTextToAppend) {
statusOutput_textArea.setText(statusOutput_textArea.getText() + "\n" + statusTextToAppend);
statusOutput_textArea.repaint();
}//End of appendStatusText method
Any help would be much appreciated :)
UPDATE
For those interested in the general structure of the solution, here it is:
public class RunTestsButtonActionListener implements ActionListener {
JTextArea statusOutput_textArea;
JButton testDatabaseSettings_JButton;
public RunTestsButtonActionListener(JTextArea statusOutput_textArea){
this.statusOutput_textArea = statusOutput_textArea;
}
public void actionPerformed(ActionEvent e) {
testDatabaseSettings_JButton = (JButton) e.getSource();
Thread tests = new Thread(){
public void run() {
//Disable button and add tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("Running Tests...");
//Run Tests
try {
statusOutput_textArea.setText("Running Tests...");
int currentTest = 1;
//Check something
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
statusOutput_textArea.append("\n Success");
//Check something else
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
statusOutput_textArea.append("\n Success");
//Other tests
.. //Run other tests here
statusOutput_textArea.append("\n\nAll tests have completed successfully. Yay.");
} finally {
//Enable button and remove tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("");
}
}
};
tests.start();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您正在对“Swing 线程”进行检查,最好在不同的 线程(如果发生/更改,则通知 GUI)。
You are doing the checks on "the Swing thread", you would be better of performing those tasks in a different thread (which notifies the GUI if something happens/changes).
在自己的线程中运行每个测试,这样它们就不会互相阻塞。另外,JTextArea已经有线程安全的
append
方法,不需要使用setText
来模拟append
。Run each test in its own thread so they don't block each other. Also, JTextArea already has an thread-safe
append
method, no need to usesetText
to simulateappend
.问题是您不能在一个操作中发生长时间运行的任务。正如您所注意到的,发生这种情况时不会有任何更新。
您应该尝试查看 SwingWorker 或其他一些后台线程。您将在后台线程中运行测试,每次您想要更新 UI 时,您都会调用:
The problem is you can't have long running tasks happen in an action. As you've noticed, nothing will get updated when this happens.
You should try taking a look at SwingWorker or some other background thread. You would run your tests in the background thread and every time you want to update the UI you would call: