使用 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)
  • 当计时器到期时,您的进度条将前进并绘制。
  • SwingWorkerdone()时(在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
        }
    }
}