我已经开发了一个小应用程序来跟踪我的日常工作活动,该工具包含两个类:
我的目标是创建一个ProgressBar来更新执行状态,使用的逻辑如下所示,
注意:将面板添加到JFrame之后,将调用setVisible(true)。
执行程序
public void executeTask () { /* Create and display the form */ progress = new UIProgress(); progress.prepareGUI(); progress.updateProgress (10); getWorkedItems (); //progress.pack (); progress.updateProgress (30); getWorkedTickets (); progress.updateProgress (50); getRemainTickets (); progress.updateProgress (70); jf.postTriagedTicketDetailsDaily(); ... }
UIProgress.java
public class UIProgress extends javax.swing.JFrame { public UIProgress() { try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } initComponents(); } private void initComponents() { panelHeading = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); progress_cntrl = new javax.swing.JProgressBar(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); panelHeading.setBackground(new java.awt.Color(204, 204, 204)); panelHeading.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); panelHeading.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION); panelHeading.setOpaque(false); jLabel1.setBackground(new java.awt.Color(0, 0, 0)); jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/devtriagerepot_daily/Background-20.jpeg"))); // NOI18N javax.swing.GroupLayout panelHeadingLayout = new javax.swing.GroupLayout(panelHeading); panelHeading.setLayout(panelHeadingLayout); panelHeadingLayout.setHorizontalGroup( panelHeadingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelHeadingLayout.createSequentialGroup() .addContainerGap(29, Short.MAX_VALUE) .addComponent(progress_cntrl, javax.swing.GroupLayout.PREFERRED_SIZE, 651, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(27, 27, 27)) .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) ); panelHeadingLayout.setVerticalGroup( panelHeadingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(panelHeadingLayout.createSequentialGroup() .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 147, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(26, 26, 26) .addComponent(progress_cntrl, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 31, Short.MAX_VALUE)) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(panelHeading, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 4, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(panelHeading, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); getAccessibleContext().setAccessibleParent(this); pack(); } public void prepareGUI () { progress_cntrl.setMaximum(120); progress_cntrl.setStringPainted(true); Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); int x = (int) ((dimension.getWidth() - this.getWidth()) / 2); int y = (int) ((dimension.getHeight() - this.getHeight()) / 2); this.setLocation(x, y); pack (); setVisible(true); } public void updateProgress (int val) { progress_cntrl.update(progress_cntrl.getGraphics()); progress_cntrl.setValue(val); }
这些方法可能是关键在这里:
getWorkedItems (); getWorkedTickets (); getRemainTickets ();
如果它们需要任何时间执行,则在Swing事件线程上调用它们将阻塞该线程并完全冻结您的GUI,从而使其无法正确绘制自己。解决方案是在后台线程中调用任何长时间运行的方法,例如SwingWorker的doInBackground()方法,并仅在Swing事件线程上进行Swing调用。同样,SwingWorker可以很好地解决此问题,实际上,它具有可以使用的自己的“绑定”进度属性。在worker中,只需调用setProgress(value)其中value是您的int从0到100的值。然后将PropertyChangeListener附加到worker,以便在progress属性更新时可以将这些更改通知给GUI。
doInBackground()
setProgress(value)
注意事项:请务必听取工作人员完成其运行的信息,以便您可以致电get()该工作人员,因为这种病使您可以捕获并响应运行过程中可能已调用的任何异常。
get()
例如,您的代码可能类似于:
public void executeTask() { progress = new UIProgress(); progress.prepareGUI(); final SwingWorker<Void, Void> myWorker = new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { // progress.updateProgress (10); setProgress(10); // sets the worker's "bound" progress property getWorkedItems(); setProgress(30); getWorkedTickets(); setProgress(50); getRemainTickets(); setProgress(70); // ... only further background work goes here // no direct Swing calls return null; } }; myWorker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { // if the progress property has been changed // get its value and use it to update the GUI progress.updateProgress((int) evt.getNewValue()); } else if (SwingWorker.StateValue.DONE == evt.getNewValue()) { // worker is done then here notify the GUI // perhaps call: // jf.postTriagedTicketDetailsDaily(); // call get() on worker to catch and handle exceptions try { myWorker.get(); } catch (InterruptedException | ExecutionException e) { // TODO handle the excpetions here e.printStackTrace(); } } } }); myWorker.execute(); }
注意:代码未经测试。
如果这不能解决您的问题,那么您可能必须创建并发布一个sscce或最小示例程序/mcve,在其中将代码压缩为仍可编译和运行的最小位,并且没有外部依赖项(例如需要(链接到数据库或图像),没有与您的问题无关的额外代码,但仍然可以演示您的问题。
例如,此小程序在工作的GUI中演示以上代码:
import java.awt.*; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.swing.*; public class TestWorker { private UIProgress progress; public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new TestWorker().executeTask(); }); } public void executeTask() { progress = new UIProgress(); progress.prepareGUI(); final SwingWorker<Void, Void> myWorker = new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { // progress.updateProgress (10); setProgress(10); // sets the worker's "bound" progress property getWorkedItems(); setProgress(30); getWorkedTickets(); setProgress(50); getRemainTickets(); setProgress(70); TimeUnit.SECONDS.sleep(2); // ... only further background work goes here // no direct Swing calls return null; } }; myWorker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { // if the progress property has been changed // get its value and use it to update the GUI progress.updateProgress((int) evt.getNewValue()); } else if (SwingWorker.StateValue.DONE == evt.getNewValue()) { // worker is done then here notify the GUI progress.updateProgress(100); // perhaps call: // jf.postTriagedTicketDetailsDaily(); // call get() on worker to catch and handle exceptions try { myWorker.get(); } catch (InterruptedException | ExecutionException e) { // TODO handle the exceptions here e.printStackTrace(); } } } }); myWorker.execute(); } // dummy methods just to demonstrate long-running code private void getRemainTickets() { mySleep(3); // emulate long-running code } private void getWorkedTickets() { mySleep(4); } private void getWorkedItems() { mySleep(2); } private void mySleep(int seconds) { try { TimeUnit.SECONDS.sleep(seconds); } catch (InterruptedException e) {} } @SuppressWarnings("serial") private class UIProgress extends JPanel { private static final int PREF_W = 400; private static final int PREF_H = 100; private JProgressBar progressBar = new JProgressBar(0, 100); private JLabel statusLabel = new JLabel(" "); public UIProgress() { JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0)); statusPanel.add(new JLabel("Status:")); statusPanel.add(Box.createHorizontalStrut(4)); statusPanel.add(statusLabel); setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); add(statusPanel, BorderLayout.PAGE_START); add(progressBar, BorderLayout.PAGE_END); } public void prepareGUI() { JFrame frame = new JFrame("UI Progress"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(this); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } @Override public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } return new Dimension(PREF_W, PREF_H); } public void updateProgress(int prog) { String text = String.format("Current Progress is %d%%", prog); statusLabel.setText(text); progressBar.setValue(prog); } } }