Dan
Dan

Reputation: 448

Binding a Timeline or Task to a ProgressBar?

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

Answers (1)

jewelsea
jewelsea

Reputation: 159331

  1. Use a ProgressBar linked to a Task when you want to display the progress of something related to the task.

    • E.g., measuring the number of bytes processed in a file upload task.
  2. Use a ProgressBar linked to a Timeline when there is no task involved.

    • E.g., displaying a countdown progress timer for the user to answer a multi-choice question in a timed quiz.

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

Related Questions