在 for 循环中更新 ProgressBar 值

Updating ProgressBar value within a for loop

我试图在 for 循环中更新 ProgressBar 值。它的进度应该每 100 毫秒更新一次,但不幸的是它没有用!这是代码:

pb = new ProgressBar(0);
Button btn = new Button("Start!");
btn.setOnAction(new EventHandler<ActionEvent>(){
    @Override
    public void handle(ActionEvent event) {
        for(int i = 0; i <=100; i++){
            position = i;
            Platform.runLater(new Runnable(){
                @Override
                public void run() {
                    pb.setProgress(position);
                    System.out.println("Index: "+position);
                }
            });
            try{
                Thread.sleep(100);
            }catch(Exception e){ System.err.println(e); }
        }
    }
});

问题 1: 我希望上面的代码每 100 毫秒更新一次进度条,但没有成功。为什么?

问题2:我在循环中调用了System.out.println("Index: "+position);。我假设输出应该是 1..2..3..4... 但我错了。输出是这样的:

Index: 100
Index: 100
Index: 100
Index: 100
Index: 100
Index: 100
Index: 100

背景

  1. Platform.runLater() 被调用以在 JavaFX Application thread 上执行某些操作,当您在其他线程上时。当您已经在 J​​avaFX 应用程序线程上将所有这些项目添加到队列中时,您每 100 毫秒 运行 宁它,因为线程已经忙于 运行 宁循环和休眠 :)

    所以它运行在线程可以自由执行它们之后,即在完成循环之后,将队列中的所有内容都删除。

  2. 还有一个问题,ProgressBar has a progressproperty它的值可以在0到1之间,0代表0%,1代表100%。如果要更新进度条,则需要将进度设置为 0.1 到 1.0 之间的值。

如何修复

您应该在按钮的操作上启动一个新线程并让它处理循环。您可以在此线程内使用 Platform.runLater,而不会阻塞 JavaFX 应用程序线程。

我会 运行 如下所示:

btn.setOnAction(event -> {
    new Thread(() -> {
         for(int i = 0; i <=100; i++){
            final int position = i;
            Platform.runLater(() -> {
                pb.setProgress(position/100.0);
                System.out.println("Index: " + position);
            });
            try{
                Thread.sleep(100);
            }catch(Exception e){ System.err.println(e); }
         }
    }).start();
});

注意: 这个回答更多地强调了 OP 哪里出了问题,以及他如何纠正它。如果您使用的是 JavaFX,您会希望使用更好的方法,即使用 Task and its updateProgress() 来更新 ProgressBar 的进度,如@kleopatra 在她的解决方案中所述。

通过手动计算和设置柱的 progressProperty 来重新发明轮子的替代方法是使用任务:它有一个 progressProperty 自动计算相对数量并保证在 FX 应用程序线程上更新, 这样就可以安全地将任何与场景相关的 属性 绑定到.

类似于:

Task<Void> task = new Task<Void>() {

    @Override
    protected Void call() throws Exception {
        double max = 137;
        for (int i = 0; i <= max; i++) {
            updateProgress(i, max);
            Thread.sleep(100);
        }
        return null;
    }

};
button.setOnAction(e -> {
    // a task is not reusable, disable
    button.setDisable(true);
    // bind "late" to not get an initial indeterminate PB
    bar.progressProperty().bind(task.progressProperty());
    new Thread(task).start();
});

显然,这是最简单的片段 - 请参阅 Task 以了解与 cancelling/interrupting 线程完美搭配的示例。