使用 jProgressBar 同步复制显示
Synchronized copying display with jProgressBar
我想监控文件从源复制到目标的进度。我使用了 synchronized
关键字,但不知何故它没有像我预期的那样工作,我的逻辑可能是错误的。如果你能帮助我,我会很高兴。
这是我的代码。
public class Download extends javax.swing.JFrame {
int val=0;
private Timer t;
private ActionListener a;
/* Creates new form Download */
public Download() {
initComponents();
jProgressBar1.setValue(val);
a = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if (jProgressBar1.getValue() < val)
jProgressBar1.setValue(jProgressBar1.getValue()+1);
else
t.stop();
}
};
}
public synchronized void copy(String source,String url)
{
try {
val+=25;
t=new Timer(200,a);
t.start();
FileInputStream fs = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(url);
int b;
while ((b = fs.read()) != -1) {
os.write(b);
}
os.close();
fs.close();
} catch (Exception E) {
E.printStackTrace();
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
String url = null;
int returnValue = chooser.showDialog(null, "Select");
if (returnValue == JFileChooser.APPROVE_OPTION) {
url = chooser.getSelectedFile().getPath();
} else {
dispose();
}
JOptionPane.showMessageDialog(this,"Wait for Completion");
if(CB1.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Ele.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB1.getText()+".pdf"));
}
if(CB2.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Mech.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB2.getText()+".pdf"));
}
if(CB3.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Phy.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB3.getText()+".pdf"));
}
if(CB4.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Civil.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB4.getText()+".pdf"));
}
JOptionPane.showMessageDialog(this,"Completed");
try {
jProgressBar1.setValue(100);
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(Download.class.getName()).log(Level.SEVERE, null, ex);
}
System.exit(0);
}
}
在这里我尝试实现这样的逻辑,每当我们调用 "copy" 方法时,它会将文件从一个位置复制到另一个位置,在此之前它应该 运行 计时器方法其中显示 jProgressBar
的进度。但不幸的是,即使在使用 synchronized
之后,它也没有显示每个文件的进度。
问题是您阻塞了 Swing 的事件调度线程 (EDT)。
Swing 会在 EDT 不忙于响应事件时进行所有绘制。在这种情况下 jButton1ActionPerformed
直到所有文件都被复制后才会返回。因此,尽管在每次 copy()
调用期间都会启动 Timer
,但计时器永远不会过期,因为 jButton1ActionPerformed
从未返回。
在这种情况下,您想使用 SwingWorker
在后台线程中复制文件。
- 当您要开始复制文件时:
- 在主线程中启动定时器
- 创建并启动
SwingWorker
。
- 打开模型对话框以阻止进一步的用户操作(或以其他方式禁用 UI)
- 当计时器到期时,您的进度条将前进并绘制。
- 当
SwingWorker
为done()
时(在EDT上执行),
- 停止计时器
- 关闭对话框(或重新启用 UI)
注意:不要从后台工作线程创建或访问任何 UI 项或 create/start/stop 计时器。这些操作只能在 EDT 上执行。
粗略示例,显示禁用 UI 元素,启动 SwingWorker
,从工作人员发布以显示进度(正在下载哪个文件),在工作人员完成时启用 UI。
使用 3 秒睡眠伪造文件副本。
package progress;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class Download extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(Download::new);
}
private final JButton downloadBtn = new JButton("Start Download");
private final JProgressBar progressBar = new JProgressBar();
private final Timer timer = new Timer(200, this::timerTick);
Download() {
super("Download Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationByPlatform(true);
downloadBtn.addActionListener(this::startDownload);
add(downloadBtn, BorderLayout.PAGE_START);
progressBar.setStringPainted(true);
add(progressBar, BorderLayout.PAGE_END);
setVisible(true);
}
private void startDownload(ActionEvent evt) {
downloadBtn.setEnabled(false);
timer.start();
DownloadWorker worker = new DownloadWorker("File1", "FileB", "AnotherFile");
worker.execute();
}
private void timerTick(ActionEvent evt) {
progressBar.setValue(progressBar.getValue()+2);
}
private class DownloadWorker extends SwingWorker<Void, String> {
private final String[] files;
DownloadWorker(String ...files) {
this.files = files;
progressBar.setValue(0);
}
@Override
protected Void doInBackground() throws Exception {
for(String file : files) {
publish(file);
// Copy the file
Thread.sleep(3000); // Pretend copy takes a few seconds
}
return null;
}
@Override
protected void process(List<String> chunks) {
String file = chunks.get(chunks.size()-1); // Just last published filename
progressBar.setString("Downloading "+file + " ...");
}
@Override
protected void done() {
progressBar.setString("Complete");
progressBar.setValue(100);
timer.stop();
downloadBtn.setEnabled(true); // Re-enable UI
}
}
}
我想监控文件从源复制到目标的进度。我使用了 synchronized
关键字,但不知何故它没有像我预期的那样工作,我的逻辑可能是错误的。如果你能帮助我,我会很高兴。
这是我的代码。
public class Download extends javax.swing.JFrame {
int val=0;
private Timer t;
private ActionListener a;
/* Creates new form Download */
public Download() {
initComponents();
jProgressBar1.setValue(val);
a = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if (jProgressBar1.getValue() < val)
jProgressBar1.setValue(jProgressBar1.getValue()+1);
else
t.stop();
}
};
}
public synchronized void copy(String source,String url)
{
try {
val+=25;
t=new Timer(200,a);
t.start();
FileInputStream fs = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(url);
int b;
while ((b = fs.read()) != -1) {
os.write(b);
}
os.close();
fs.close();
} catch (Exception E) {
E.printStackTrace();
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
String url = null;
int returnValue = chooser.showDialog(null, "Select");
if (returnValue == JFileChooser.APPROVE_OPTION) {
url = chooser.getSelectedFile().getPath();
} else {
dispose();
}
JOptionPane.showMessageDialog(this,"Wait for Completion");
if(CB1.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Ele.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB1.getText()+".pdf"));
}
if(CB2.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Mech.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB2.getText()+".pdf"));
}
if(CB3.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Phy.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB3.getText()+".pdf"));
}
if(CB4.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Civil.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\"+CB4.getText()+".pdf"));
}
JOptionPane.showMessageDialog(this,"Completed");
try {
jProgressBar1.setValue(100);
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(Download.class.getName()).log(Level.SEVERE, null, ex);
}
System.exit(0);
}
}
在这里我尝试实现这样的逻辑,每当我们调用 "copy" 方法时,它会将文件从一个位置复制到另一个位置,在此之前它应该 运行 计时器方法其中显示 jProgressBar
的进度。但不幸的是,即使在使用 synchronized
之后,它也没有显示每个文件的进度。
问题是您阻塞了 Swing 的事件调度线程 (EDT)。
Swing 会在 EDT 不忙于响应事件时进行所有绘制。在这种情况下 jButton1ActionPerformed
直到所有文件都被复制后才会返回。因此,尽管在每次 copy()
调用期间都会启动 Timer
,但计时器永远不会过期,因为 jButton1ActionPerformed
从未返回。
在这种情况下,您想使用 SwingWorker
在后台线程中复制文件。
- 当您要开始复制文件时:
- 在主线程中启动定时器
- 创建并启动
SwingWorker
。 - 打开模型对话框以阻止进一步的用户操作(或以其他方式禁用 UI)
- 当计时器到期时,您的进度条将前进并绘制。
- 当
SwingWorker
为done()
时(在EDT上执行),- 停止计时器
- 关闭对话框(或重新启用 UI)
注意:不要从后台工作线程创建或访问任何 UI 项或 create/start/stop 计时器。这些操作只能在 EDT 上执行。
粗略示例,显示禁用 UI 元素,启动 SwingWorker
,从工作人员发布以显示进度(正在下载哪个文件),在工作人员完成时启用 UI。
使用 3 秒睡眠伪造文件副本。
package progress;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class Download extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(Download::new);
}
private final JButton downloadBtn = new JButton("Start Download");
private final JProgressBar progressBar = new JProgressBar();
private final Timer timer = new Timer(200, this::timerTick);
Download() {
super("Download Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationByPlatform(true);
downloadBtn.addActionListener(this::startDownload);
add(downloadBtn, BorderLayout.PAGE_START);
progressBar.setStringPainted(true);
add(progressBar, BorderLayout.PAGE_END);
setVisible(true);
}
private void startDownload(ActionEvent evt) {
downloadBtn.setEnabled(false);
timer.start();
DownloadWorker worker = new DownloadWorker("File1", "FileB", "AnotherFile");
worker.execute();
}
private void timerTick(ActionEvent evt) {
progressBar.setValue(progressBar.getValue()+2);
}
private class DownloadWorker extends SwingWorker<Void, String> {
private final String[] files;
DownloadWorker(String ...files) {
this.files = files;
progressBar.setValue(0);
}
@Override
protected Void doInBackground() throws Exception {
for(String file : files) {
publish(file);
// Copy the file
Thread.sleep(3000); // Pretend copy takes a few seconds
}
return null;
}
@Override
protected void process(List<String> chunks) {
String file = chunks.get(chunks.size()-1); // Just last published filename
progressBar.setString("Downloading "+file + " ...");
}
@Override
protected void done() {
progressBar.setString("Complete");
progressBar.setValue(100);
timer.stop();
downloadBtn.setEnabled(true); // Re-enable UI
}
}
}