I'm having trouble getting swingworker to work when copying files recursively. No matter what I do, my progress bar doesn't update until after all the files are copied. The direction I chose to do is that I calculate the total number of files. I then have a JLabel that lists the number of files copied out of the total number of files. I then want a progressbar to show the percentage of the files copied.
This is my current code that I have (very basic app, i know)
package swingworkertest;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.SwingWorker;
public class App extends JFrame {
private JPanel mainPanel;
private JButton button;
private JLabel label;
private JProgressBar progress;
private int totalFiles = 0;
private int copiedFiles = 0;
private void getTotalFiles(File src) {
String[] files = src.list();
for (int i = 0; i < files.length; i++) {
if (new File(src + "\\" + files[i]).isDirectory()) {
getTotalFiles(new File(src + "\\" + files[i]));
} else {
totalFiles++;
}
}
}
private void copyFiles(File src, File dst) throws IOException {
String[] files = src.list();
for (int i = 0; i < files.length; i++) {
if (new File(src + "\\" + files[i]).isDirectory()) {
if (Files.notExists(new File(dst + "\\" + files[i]).toPath(), LinkOption.NOFOLLOW_LINKS)) {
new File(dst + "\\" + files[i]).mkdir();
}
copyFiles(new File(src + "\\" + files[i]), new File(dst + "\\" + files[i]));
} else {
Files.copy(new File(src + "\\" + files[i]).toPath(), new File(dst + "\\" + files[i]).toPath());
copiedFiles++;
label.setText("Files Copied: " + copiedFiles + "/" + totalFiles);
}
}
}
private void setProgress(int value) {
progress.setValue(value);
}
public App(String title) {
this.setTitle(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String src = "C:\\Users\\Vinny\\Desktop\\workingDirectory";
String dst = "C:\\Users\\Vinny\\Desktop\\copyToHere";
mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
button = new JButton("Copy");
label = new JLabel("Files Copied: 0/0");
progress = new JProgressBar();
progress.setStringPainted(true);
button.addActionListener((e) -> {
getTotalFiles(new File(src));
System.out.println(totalFiles);
SwingWorker<Boolean, Integer> worker = new SwingWorker<>() {
@Override
protected Boolean doInBackground() throws Exception {
// Background Work
copyFiles(new File(src), new File(dst));
publish(copiedFiles);
// Value transmitted to done()
return true;
}
@Override
protected void process(List<Integer> chunks) {
// Process results
for (int n : chunks) {
int percentage = (int)(n * 100 / totalFiles);
setProgress(percentage);
}
}
@Override
protected void done() {
// Finish sequence
System.out.println("Files Copied");
}
};
worker.execute();
while (!worker.isDone()) {
int percentage = (int)(copiedFiles * 100 / totalFiles);
setProgress(percentage);
}
});
mainPanel.add(button);
mainPanel.add(label);
mainPanel.add(progress);
this.add(mainPanel);
this.pack();
this.setVisible(true);
}
}
This is almost certainly the problem.
You're updating the progress on the EDT. Basically that loop is holding onto the UI thread, not letting it do other work. You have to update from the back ground thread if you want the EDT to be able to actually execute.
Here's an example from the tutorial, I'd use the
publish()andprocess()methods to update the GUI as the files are read.https://docs.oracle.com/javase/tutorial/uiswing/concurrency/interim.html