来自SwingWorker的jProgressBar更新

我用来通过更新ProgressBar来监视长时间运行的任务。 长时间运行的任务当然是在Swingworker线程中执行的。

我过去经常编程:

public class MySwingWorkerClass extends SwingWorker { private JProgressBar progressBar; public MySwingWorker(JProgressBar aProgressBar) { this.progressBar = aProgressBar; progressBar.setVisible(true); progressBar.setStringPainted(true); progressBar.setValue(0); } @Override public Void doInBackground() { //long running task loop { calculation(); progressBar.setValue(value); } return null; } @Override public void done() { progressBar.setValue(100); progressBar.setStringPainted(false); progressBar.setVisible(false); } } 

但最近我发现我可以通过使用“setProgress”并定义属性更改并执行类似的操作来实现

 public class MySwingWorkerClass extends SwingWorker { private JProgressBar progressBar; public MySwingWorker(JProgressBar aProgressBar) { addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { progressBar.setValue((Integer) evt.getNewValue()); } } }); progressBar.setVisible(true); progressBar.setStringPainted(true); progressBar.setValue(0); setProgress(0); } @Override public Void doInBackground() { //long running task loop { calculation(); setProgress(value); } return null; } @Override public void done() { setProgress(100); progressBar.setValue(100); progressBar.setStringPainted(false); progressBar.setVisible(false); } } 

我的问题是:我的第一个代码是可接受的还是我应该使用setProgress进行任何调整? 我发现第二个代码更复杂,在我的情况下,并不知道是否有任何优势或理由使用第二个。

有什么建议吗?

编辑感谢您的回答。 作为总结。 第一种解决方案是“错误的”,因为进度条更新是在EDT之外执行的。 第二种解决方案是“正确的”,因为进度条更新是在EDT内执行的

现在,根据@mKorbel的“有趣”答案,在我的情况下,我的计算结果是HTML文本,我“插入”(见此链接 )。 我目前的代码如下。

我发布(字符串),我的流程代码就是这样

 @Override protected void process(List strings) { for (String s : strings) { try { htmlDoc.insertBeforeEnd(htmlDoc.getElement(htmlDoc.getDefaultRootElement(), StyleConstants.NameAttribute, HTML.Tag.TABLE), s); } catch (BadLocationException ex) { } catch (IOException ex) { } } } 

在我的情况下,我如何重复使用@mKobel来做同样的事情。 我的意思是他在我的情况下使用覆盖表渲染我应该覆盖什么渲染器(jTextPane?)以及如何?

在第一个代码中,您在非EDT(事件调度程序线程)线程中调用以下行。 所以它不是线程安全的:

 progressBar.setValue(value); 

这可能会导致意外行为,因为Swing未设计为线程安全库。

有不同的方法以Swing方式执行此操作。 一个正确的方法就是你在第二篇文章中所做的。 另一种方法是使用publish()/process()方法,第三种方法是编写自己的线程而不是SwingWorker并使用SwingUtilities.invokeLater()

我用来通过更新ProgressBar来监视长时间运行的任务。 长时间运行的任务当然是在Swingworker线程中执行的。

您可以在所有情况下使用SwingWorker将任何繁重且长时间运行的任务重定向到Background

 import java.awt.*; import java.util.*; import javax.swing.*; import javax.swing.table.*; public class TableCellProgressBar { private String[] columnNames = {"String", "ProgressBar"}; private Object[][] data = {{"dummy", 100}}; private DefaultTableModel model = new DefaultTableModel(data, columnNames) { private static final long serialVersionUID = 1L; @Override public Class getColumnClass(int column) { return getValueAt(0, column).getClass(); } @Override public boolean isCellEditable(int row, int col) { return false; } }; private JTable table = new JTable(model); public JComponent makeUI() { TableColumn column = table.getColumnModel().getColumn(1); column.setCellRenderer(new ProgressRenderer()); EventQueue.invokeLater(new Runnable() { @Override public void run() { startTask("test"); startTask("error test"); startTask("test"); } }); JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table)); return p; } //http://java-swing-tips.blogspot.com/2008/03/jprogressbar-in-jtable-cell.html private void startTask(String str) { final int key = model.getRowCount(); SwingWorker worker = new SwingWorker() { private int sleepDummy = new Random().nextInt(100) + 1; private int lengthOfTask = 120; @Override protected Integer doInBackground() { int current = 0; while (current < lengthOfTask && !isCancelled()) { if (!table.isDisplayable()) { break; } if (key == 2 && current > 60) { //Error Test cancel(true); publish(-1); return -1; } current++; try { Thread.sleep(sleepDummy); } catch (InterruptedException ie) { break; } publish(100 * current / lengthOfTask); } return sleepDummy * lengthOfTask; } @Override protected void process(java.util.List c) { model.setValueAt(c.get(c.size() - 1), key, 1); } @Override protected void done() { String text; int i = -1; if (isCancelled()) { text = "Cancelled"; } else { try { i = get(); text = (i >= 0) ? "Done" : "Disposed"; } catch (Exception ignore) { ignore.printStackTrace(); text = ignore.getMessage(); } } System.out.println(key + ":" + text + "(" + i + "ms)"); } }; model.addRow(new Object[]{str, 0}); worker.execute(); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } public static void createAndShowGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.getContentPane().add(new TableCellProgressBar().makeUI()); frame.setSize(320, 240); frame.setLocationRelativeTo(null); frame.setVisible(true); } } class ProgressRenderer extends DefaultTableCellRenderer { private final JProgressBar b = new JProgressBar(0, 100); public ProgressRenderer() { super(); setOpaque(true); b.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Integer i = (Integer) value; String text = "Completed"; if (i < 0) { text = "Error"; } else if (i < 100) { b.setValue(i); return b; } super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column); return this; } } 

但是为什么使用SwingWorker复杂化Wwing GUI(对Java Essential ClassesGenerics也需要最深入的了解),

Runnable#Thread基本实现只需要invokeLater来输出到Swing GUI,并且在从EDT(来自Swing / AWT Listener)开始的情况下,并且没有任何代码行包含Thread.sleep(int)那么invokeLater只建议/生产代码所需

 import java.awt.Component; import java.util.Random; import javax.swing.JFrame; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; public class TableWithProgressBars { public static class ProgressRenderer extends JProgressBar implements TableCellRenderer { private static final long serialVersionUID = 1L; public ProgressRenderer(int min, int max) { super(min, max); this.setStringPainted(true); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { this.setValue((Integer) value); return this; } } private static final int maximum = 100; public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TableWithProgressBars().createGUI(); } }); } public void createGUI() { final JFrame frame = new JFrame("Progressing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Integer[] oneRow = {0, 0, 0, 0}; String[] headers = {"One", "Two", "Three", "Four"}; Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,}; final DefaultTableModel model = new DefaultTableModel(data, headers); final JTable table = new JTable(model); table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum)); table.setPreferredScrollableViewportSize(table.getPreferredSize()); frame.add(new JScrollPane(table)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); new Thread(new Runnable() { @Override public void run() { Object waiter = new Object(); synchronized (waiter) { int rows = model.getRowCount(); int columns = model.getColumnCount(); Random random = new Random(System.currentTimeMillis()); boolean done = false; while (!done) { int row = random.nextInt(rows); int column = random.nextInt(columns); Integer value = (Integer) model.getValueAt(row, column); value++; if (value <= maximum) { model.setValueAt(value, row, column); try { waiter.wait(15); } catch (InterruptedException e) { e.printStackTrace(); } } done = true; for (row = 0; row < rows; row++) { for (column = 0; column < columns; column++) { if (!model.getValueAt(row, column).equals(maximum)) { done = false; break; } } if (!done) { break; } } } frame.setTitle("All work done"); } } }).start(); } } 

对于真正重型和长期运行任务的结论,你看看Runnable#Thread #Thread(简单,简单,非笨拙和清晰的方式),只有你对JavaSwing了解非常好,那么你才能想到SwingWorker

您的第二种方法是正确的,甚至记录在SwingWorker类的类javadoc中。 “进度”事件在EDT上被触发,因此您的侦听器会更新EDT上的进度条。 在您的第一种方法中情况并非如此。

另一种方法的例子(使用publish/process如vizier所示)可以在我之前的SO问题的答案中找到

如本例所示,在第二个示例中使用worker的setProgress()是正确的:任何PropertyChangeListener都将在事件派发线程上异步通知。