我有一个包含JLabel的模态对话框。此对话框调用另一个类中的方法,该类执行长时间运行的查找活动。我希望这个查找类能够更新从模态对话框传递的JLabel。
我尝试在查找过程中从代码循环中更新JLabel。 (我发现当模式对话框运行时,永远不会调用Swing计时器的actionPerformed方法。)自实现Timer以来,我将查找过程移到了一个单独的Thread。
我的查找工作正常,但JLabel在流程结束前仍然没有重新绘制。我会感激任何帮助。
这是相关的方法。 DictionaryTuple是一个POJO,包含两个字段:List和String。只是getter和setter,但是dictionaryThread确实可以访问tupleList;请注意,在dictionaryThread完成之前,我们不会触摸它。
public List<DictionaryTuple> findTuples() {
tupleList = new ArrayList<DictionaryTuple>();
Thread dictionaryThread = new Thread(this); // This is the long-running lookup thread
dictionaryThread.start();
progressTimer = new Timer();
progressTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// progressCaption is updated in dictionaryThread
synchronized (progressCaption) {
lblProgress.setText(progressCaption);
}
lblProgress.repaint();
Thread.yield();
}
}, 250, 250);
try {
dictionaryThread.join();
} catch (InterruptedException e) {
} finally {
progressTimer.cancel();
lblProgress.setText("");
progressCaption = "";
}
return tupleList;
}
我也尝试过这种变化;在这种情况下,内部run()调用将保持到处理完成为止:
progressTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
synchronized (progressCaption) {
System.out.println("Fire!");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Update");
lblProgress.setText(progressCaption);
lblProgress.repaint();
}});
}
}
}, 250, 250);
答案 0 :(得分:2)
您需要向我们展示 ,您将对话框设置为可见 - 是在调用findTuples()
方法之前还是之后?这很关键。
<强>建议:强>
答案 1 :(得分:2)
这个演示应该会有所帮助。如果我通过调用run vs在一个单独的线程中运行它来运行长作业,请查看不同的内容
public static void modalDialogTest() {
JDialog dialog = new JDialog((JFrame)null);
final JLabel jl = new JLabel("Need some space");
JButton jb = new JButton ("Click me");
Timer t = new Timer(1000, new AbstractAction() {
int it = 0;
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
jl.setText("" + it);
jl.repaint();
it++;
}
});
t.start();
AbstractAction wait = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Runnable r = new Runnable() {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//r.run();
new Thread(r).start();
}
};
jb.addActionListener(wait);
dialog.getContentPane().add(jl);
dialog.getContentPane().add(jb);
dialog.getContentPane().setLayout(new FlowLayout());
dialog.pack();
dialog.setVisible(true);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
}
答案 2 :(得分:1)
谢谢大家的建议。我实现的是Hovercraft,使用SwingWorker。
我的方法所在的Dictionary类现在扩展了SwingWorker。 findTuples()方法变为doInBackground()。不再需要Timer和我自己的Thread代码。
doInBackground()使用带有属性名称“progress”的publish()来提供要在更新的JLabel中显示的文本。
返回对话框代码,调用涉及创建一个Dictionary实例,并使用PropertyChangeListener处理JLabel中的更改(名为lblProgress)并检测处理何时完成:
private void myCallingMethod() {
final Dictionary dictionary = new Dictionary();
dictionary.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
List<DictionaryTuple> tupleList;
// Is processing complete?
if (event.getNewValue() instanceof StateValue
&& event.getNewValue() == StateValue.DONE) {
try {
tupleList = dictionary.get();
lblProgress.setText(tupleList.size() + " Tuples Found");
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"Error in processing dictionary: "
+ e.getMessage());
tupleList = new ArrayList<DictionaryTuple>();
lblProgress.setText("");
}
// Process the results.
processTuples(tupleList);
} else {
// The process isn't done yet.
// Is this a request to change lblProgress?
if (event.getPropertyName().equals("progress")) {
lblProgress.setText((String) event.getNewValue());
}
}
}
});
// Start the dictionary lookup to run asynchronously.
dictionary.execute();
}
我学会了不依赖SwingWorker.isDone()来确定SwingWorker的处理是否完整。在我的例子中,PropertyChangeListener在该状态下被调用两次:一次是使用上一次发布()调用的结果,一次是在任务实际完成时。