Java GUI - 在异步任务完成之前进度条不会更新
Java GUI - Progress bar doesn't update until the async task is finished
我正在使用 CompletableFuture 运行 一个长 运行ning 操作。同时,我使用 SwingWorker 以 5 为增量更新进度条。
JProgressBar progressBar = new JProgressBar();
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
CompletableFuture<NetworkToolsTableModel> completableFuture = CompletableFuture.supplyAsync(() -> asyncTask());
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int val = 0;
while (!completableFuture.isDone()) {
if (val < 100) {
val += 5;
progressBar.setValue(val);
}
Rectangle bounds = progressBar.getBounds(null);
progressBar.paintImmediately(bounds);
}
return null;
}
};
worker.execute();
在异步方法完成之前,进度条不会更新。我也尝试在 EDT 线程上执行此操作,但无济于事。你所看到的基本上是我在这一点上尝试做试错。
为什么进度条没有更新?我该如何解决这个问题?
这将更新进度条,但它会立即达到 100%,因为 SwingWorker 正在不断循环。
您应该通过轮询任务或(更好)使用观察者模式从异步任务中获取实际进度值。
后一种情况可以去掉swing worker,直接在观察者回调方法中更新进度条。
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int val = 0;
while (!completableFuture.isDone()) {
if (val < 100) {
val += 5;
final int prog=val;
SwingUtilities.invokeLater(()->{progressBar.setValue(prog); });
}
}
return null;
}
};
这是一个完整的观察者模式示例,我使用 PropertyChangeSupport 定义了一个可观察的 class ALongTask,该任务将其进度值通知给所有已注册的观察者(即 PropertyChangeListener)。
package test;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class TestProgressBar {
public static void main(String[] args) {
JProgressBar progressBar = new JProgressBar();
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
ALongTask asyncTask=new ALongTask();
asyncTask.addPropertyChangeListener((e)-> { SwingUtilities.invokeLater(()->{progressBar.setValue((Integer) e.getNewValue()); }); });
CompletableFuture.supplyAsync(asyncTask);
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(progressBar);
frame.pack();
frame.setVisible(true);
}
public static class ALongTask implements Supplier<Object> {
PropertyChangeSupport support=new PropertyChangeSupport(this);
protected int progress;
public void setProgress(int progress) {
int old=this.progress;
this.progress=progress;
support.firePropertyChange("progress", old, progress);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
@Override
public Object get() {
for (int i=0;i<20;i++) {
setProgress(i*5);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
return null;
}
}
setProgress(100);
return new Object();
}
}
}
停下来仔细看看Worker Threads and SwingWorker。
A SwingWorker
旨在允许您执行较长的 运行 任务,这可能会产生中间结果,并允许您将这些结果 publish
返回给事件调度线程安全处理(通过 process
方法)。
你“可以”publish
在process
方法中更新进度条,但是SwingWorker
已经提供了progress
属性,你可以监控。查看 SwingWorker
JavaDocs 中的一些现成示例!
运行 例子
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
ProgressPane progressPane = new ProgressPane();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(progressPane);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// progressPane.doWork();
}
});
}
public class ProgressPane extends JPanel {
private JProgressBar progressBar;
private JButton startButton;
public ProgressPane() {
setLayout(new GridBagLayout());
progressBar = new JProgressBar();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
add(progressBar, gbc);
startButton = new JButton("Start");
gbc.gridy = 1;
add(startButton, gbc);
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
startButton.setEnabled(false);
doWork();
}
});
}
public void doWork() {
Worker worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
public class Worker extends SwingWorker<Object, Object> {
@Override
protected void done() {
startButton.setEnabled(true);
}
@Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = Math.round(((float) index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(10);
}
return null;
}
}
}
}
我正在使用 CompletableFuture 运行 一个长 运行ning 操作。同时,我使用 SwingWorker 以 5 为增量更新进度条。
JProgressBar progressBar = new JProgressBar();
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
CompletableFuture<NetworkToolsTableModel> completableFuture = CompletableFuture.supplyAsync(() -> asyncTask());
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int val = 0;
while (!completableFuture.isDone()) {
if (val < 100) {
val += 5;
progressBar.setValue(val);
}
Rectangle bounds = progressBar.getBounds(null);
progressBar.paintImmediately(bounds);
}
return null;
}
};
worker.execute();
在异步方法完成之前,进度条不会更新。我也尝试在 EDT 线程上执行此操作,但无济于事。你所看到的基本上是我在这一点上尝试做试错。
为什么进度条没有更新?我该如何解决这个问题?
这将更新进度条,但它会立即达到 100%,因为 SwingWorker 正在不断循环。
您应该通过轮询任务或(更好)使用观察者模式从异步任务中获取实际进度值。
后一种情况可以去掉swing worker,直接在观察者回调方法中更新进度条。
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
int val = 0;
while (!completableFuture.isDone()) {
if (val < 100) {
val += 5;
final int prog=val;
SwingUtilities.invokeLater(()->{progressBar.setValue(prog); });
}
}
return null;
}
};
这是一个完整的观察者模式示例,我使用 PropertyChangeSupport 定义了一个可观察的 class ALongTask,该任务将其进度值通知给所有已注册的观察者(即 PropertyChangeListener)。
package test;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class TestProgressBar {
public static void main(String[] args) {
JProgressBar progressBar = new JProgressBar();
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
ALongTask asyncTask=new ALongTask();
asyncTask.addPropertyChangeListener((e)-> { SwingUtilities.invokeLater(()->{progressBar.setValue((Integer) e.getNewValue()); }); });
CompletableFuture.supplyAsync(asyncTask);
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(progressBar);
frame.pack();
frame.setVisible(true);
}
public static class ALongTask implements Supplier<Object> {
PropertyChangeSupport support=new PropertyChangeSupport(this);
protected int progress;
public void setProgress(int progress) {
int old=this.progress;
this.progress=progress;
support.firePropertyChange("progress", old, progress);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
@Override
public Object get() {
for (int i=0;i<20;i++) {
setProgress(i*5);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
return null;
}
}
setProgress(100);
return new Object();
}
}
}
停下来仔细看看Worker Threads and SwingWorker。
A SwingWorker
旨在允许您执行较长的 运行 任务,这可能会产生中间结果,并允许您将这些结果 publish
返回给事件调度线程安全处理(通过 process
方法)。
你“可以”publish
在process
方法中更新进度条,但是SwingWorker
已经提供了progress
属性,你可以监控。查看 SwingWorker
JavaDocs 中的一些现成示例!
运行 例子
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
ProgressPane progressPane = new ProgressPane();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(progressPane);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// progressPane.doWork();
}
});
}
public class ProgressPane extends JPanel {
private JProgressBar progressBar;
private JButton startButton;
public ProgressPane() {
setLayout(new GridBagLayout());
progressBar = new JProgressBar();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
add(progressBar, gbc);
startButton = new JButton("Start");
gbc.gridy = 1;
add(startButton, gbc);
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
startButton.setEnabled(false);
doWork();
}
});
}
public void doWork() {
Worker worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
public class Worker extends SwingWorker<Object, Object> {
@Override
protected void done() {
startButton.setEnabled(true);
}
@Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = Math.round(((float) index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(10);
}
return null;
}
}
}
}