Reputation: 448
Is there a "preferred" method between Tasks or JavaFX timelines when implementing the progressProperty
binding of JavaFX progress bars/indicators?
For example, this code loads a screen with 2 progress bars triggered by a button. One (progressBar
) has its progress bound by a Task, whereas the other (progressBar2
) is by a timeline.
public class TaskTest extends Application {
public static void main(String[] args) throws Exception { launch(args); }
public void start(final Stage stage) throws Exception {
final Button runButton = new Button("Run");
final ProgressBar progressBar = new ProgressBar();
final ProgressBar progressBar2 = new ProgressBar();
Integer START_TIME = 11;
Timeline timeline = new Timeline();
IntegerProperty timeSeconds = new SimpleIntegerProperty((START_TIME) * 100);
runButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
final Task task = new Task<Object>() {
@Override
protected Object call() throws InterruptedException {
for (int i = 0; i < 11; i++) {
Thread.sleep(1000);
updateProgress(i+1, 11);
}
return true;
}
};
runButton.disableProperty().bind(task.runningProperty());
progressBar.progressProperty().bind(task.progressProperty());
task.stateProperty().addListener(new ChangeListener<Worker.State>() {
@Override
public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
System.out.println("This is ok, this thread " + Thread.currentThread() + " is the JavaFX Application thread.");
runButton.setText("Voila!");
}
}
});
new Thread(task).start();
//Timeline
progressBar2.progressProperty().bind(timeSeconds.divide((START_TIME) * 100.0).subtract(1).multiply(-1));
if (timeline != null){
timeline.stop();
}
timeSeconds.set((START_TIME) * 100);
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(START_TIME), new KeyValue(timeSeconds, 0)));
timeline.playFromStart();
timeline.setOnFinished(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event) {
System.out.println("DONE");
}
});
}
});
VBox layout = new VBox();
layout.getChildren().addAll(runButton,progressBar,progressBar2);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding:10; -fx-font-size: 16;");
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
}
progressBar2
has more 'flow' compared to progressBar
which is updating in 'chunks'. This can be prevented by simply changing reducing the value of `Thread.sleep(1000)'. Is this good practice though?
It seems to me that using timelines are simpler code wise and more effective than using Tasks.
What should I be using and more importantly why??
Upvotes: 0
Views: 1138
Reputation: 159331
Use a ProgressBar linked to a Task when you want to display the progress of something related to the task.
Use a ProgressBar linked to a Timeline when there is no task involved.
A task will be required for a lengthy computation or blocking I/O process performed on another thread. If you don't have either of those, then you don't need a task.
When you do have a task, the task API provides a progress property to which you can bind your the value of your progress bar in a thread safe manner.
The progress of a task need not be related to a fixed time period in which the progress varies at a constant rate. For example, the throughput of bytes transferred for a file upload and the total time for the file upload could vary based upon inconsistent network speeds. By linking the progress of the task to the number of bytes transferred, the progress bar will show the true measure of the total work done for the task. This information couldn't accurately be derived just using a timeline.
If you use a timeline, then you can specify the progress bar's value property as a a KeyValue in the timeline, which you may find simpler than the code you are currently using.
Upvotes: 1