I've looked around and I cannot find any decent snippets that show you how to create accurate progress bars. All of the ones I've seen are cheap hacking them by using Thread.sleep(10)
to indicate some workload on the thread performing "work". This is extremely inaccurate in some cases because it doesn't actually take into account a threads workload. I also don't want to use a indeterminate progress bar because those look bad.
My question is: How can you create an accurate progress bar (non-indeterminate) that runs parallel with the thread doing all the "work" in this case writing several thousand images to a directory. If you don't know the work load of the thread and you don't know how long it will take to actually dump all of them. What is the best way to approach this? I'm trying to make this realistic and not showing the progress bar just for cosmetics.
So let's say I have a program like this.
@FXML
private void dump(ActionEvent e) {
try {
if (e.getSource() == spriteDumpMI) {
Thread thread = new Thread(() -> {
try (Cache cache = new Cache(FileStore.open(Constants.CACHE_PATH))) {
ReferenceTable table = ReferenceTable.decode(Container
.decode(cache.getStore().read(255, 8)).getData());
Platform.runLater(() -> {
createTask(10, table.capacity());
});
for (int i = 0; i < table.capacity(); i++) {
if (table.getEntry(i) == null)
continue;
Container container = cache.read(8, i);
Sprite sprite = Sprite.decode(container.getData());
File dir = new File(Constants.SPRITE_PATH);
if (!dir.exists()) {
dir.mkdir();
}
for (int frame = 0; frame < sprite.size(); frame++) {
File file = new File(Constants.SPRITE_PATH,
i + "_" + frame + ".png");
BufferedImage image = sprite.getFrame(frame);
ImageIO.write(image, "png", file);
}
}
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
});
thread.start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
The program is simple, basically it writes all of the sprites that are found in an archive (can be several thousand images) and writes all of them to a directory so the user can use them.
I'm updating my progress bar by using this.
Platform.runLater(() -> {
// this is bad
createTask(10, table.capacity());
});
I understand I could update the progress bar in the loop, but I'm not sure how to do that without creating a ton of threads and new tasks (which is also really bad)
And the method createTask
private void createTask(int workload, int max) {
Task<Boolean> task = taskCreator(workload, max);
progressBar.setVisible(true);
progressI.setVisible(true);
progressBar.progressProperty().unbind();
progressBar.progressProperty().bind(task.progressProperty());
progressI.progressProperty().unbind();
progressI.progressProperty().bind(task.progressProperty());
new Thread(task).start();
}
private Task<Boolean> taskCreator(int workload, int max) {
return new Task<Boolean>() {
@Override
protected Boolean call() throws Exception {
progressText.setText("In Progress");
progressText.setFill(Color.WHITE);
for (int index = 0; index < max; index++) {
Thread.sleep(workload);
updateProgress(index, max);
}
progressText.setText("Complete!");
progressText.setFill(Color.GREEN);
Thread.sleep(1000);
progressBar.setVisible(false);
progressI.setVisible(false);
progressText.setText("");
return true;
}
};
}