JavaFX refresh Progress Bar within a method

2019-07-19 09:40发布

问题:

I have a javafx Progressbar in my UI and then I have a for loop. In every iteration of the for loop this happens:

progressVal += 5.0;
progress.setProgress(progressVal);

And after the whole loop, this happens:

progress.setProgress(100);

I now have recognized that the UI controls are only refreshed after the method has finished, so the progress bar wont update until the end and than would be set to 100 imidiatly.

So is it possible so somehow force the UI update, even when the method isn`t finished completly? Or how else could I do this?

回答1:

If what you do in your loop takes a long time and should not be executed on the JavaFX application thread, (which it probably does or you probably wouldn't have this question), then you should probably run the loop in a Task, on a different thread, updating the task's progress as the loop progresses and binding the progress bar's value to the task's progress.

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ProgressFeedback extends Application {
    private static final double EPSILON = 0.0000005;
    @Override
    public void start(Stage stage) throws Exception {
        final Task<Void> task = new Task<Void>() {
            final int N_ITERATIONS = 100;

            @Override
            protected Void call() throws Exception {
                for (int i = 0; i < N_ITERATIONS; i++) {
                    updateProgress(i + 1, N_ITERATIONS);
                    // sleep is used to simulate doing some work which takes some time....
                    Thread.sleep(10);
                }

                return null;
            }
        };

        final ProgressBar progress = new ProgressBar();
        progress.progressProperty().bind(
                task.progressProperty()
        );
        // color the bar green when the work is complete.
        progress.progressProperty().addListener(observable -> {
            if (progress.getProgress() >= 1 - EPSILON) {
                progress.setStyle("-fx-accent: forestgreen;");
            }
        });

        // layout the app
        final StackPane layout = new StackPane(progress);
        layout.setPadding(new Insets(10));
        stage.setScene(new Scene(layout));
        stage.show();

        final Thread thread = new Thread(task, "task-thread");
        thread.setDaemon(true);
        thread.start();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

what if the task makes call to a method that does only a single job , and do not want it to be called again and again?

Then remove the loop and invoke updateProgress through multiple calls within the job as the job progresses. The loop is only present in this sample for demonstration purposes and because this is what the original question specifically asked about.

For example lets say your job has 3 stages: fetch from database, perform calculation and accumulate results. Each stage taking an estimated 30 percent, 60 percent and 10 percent of total time respectively, then you can do something like this within the call() body of your task:

updateProgress(0, 1.0);
Data data = db.fetchData(query);
updateProgress(0.3, 1.0);
ProcessedData processed = calculator.process(data);
updateProgress(0.9, 1.0);
Result result = accumulator.reduce(processed);
updateProgress(1.0, 1.0);


回答2:

Quotation from Java Doc:

The actual progress of the ProgressIndicator. A negative value for progress indicates that the progress is indeterminate. A positive value between 0 and 1 indicates the percentage of progress where 0 is 0% and 1 is 100%. Any value greater than 1 is interpreted as 100%.

That means in simple words, you must divide you value with 100, or you just increment the value by 0.05 instead of 0.5.

Happy Coding,
Kalasch



回答3:

My friend show me example of using that. You could do this:

Platform.runLater(new Runnable() {
            @Override public void run() {
                bar.setProgress(counter/1000000.0);
            }
        });

And the refresh progress bar is in other Thread