JavaFX更新进度条并等待线程完成

4

我正在尝试在Java FX中更新进度条。我的第一个问题是窗口显示“未响应”,而不是实际更新。它只是冻结,然后在任务完成后,进度条变成了满的。所以我发现我必须使用多线程并像这样实现它。

最初的回答:

我正在尝试在Java FX中更新进度条。我的第一个问题是窗口显示“未响应”,而不是实际更新。它只是冻结,然后在任务完成后,进度条变成了满的。所以我发现我必须使用多线程并像这样实现它。

overallList.clear();
progressbar.setprogress(0);

for(Object obj : list) {
    class ThreadProgress implements Runnable { // inner class
        public void run() {
            thisList = scrape(obj);
            overallList.add(thisList);
            progressbar.setProgress(progressbar.getProgress() + (double)1/size);
        }
    }

    Thread current = new Thread(new ThreadProgress());
    current.start();
}

textAreaConsole.setText("Total number of things:" + overallList.size());

但是现在的问题是最后一行打印出"Total number of things: 0",因为线程在机器运行最后一行之前就已经执行完成了。然后我发现可以通过多种方式来解决这个问题,具体来说是使用join()或ExecutorService。我像这样实现了join()。


overallList.clear();
progressbar.setprogress(0);
List<Thread> threads = new ArrayList<Thread>();

for(Object obj : list) {
    class ThreadProgress implements Runnable { // inner class
        public void run() {
            thisList = scrape(obj);
            overallList.add(thisList);
            progressbar.setProgress(progressbar.getProgress() + (double)1/size);
        }
    }

    Thread current = new Thread(new ThreadProgress());
    current.start();
    threads.add(current);
}

for(Thread thread : threads) thread.join(); // with a try-catch loop

textAreaConsole.setText("Total number of things:" + overallList.size());

但这又让我回到最初的问题,窗口再次显示“未响应”。使用ExecutorService时也出现了同样的问题。现在我不知道该怎么办了。


请提供一个[mcve]来演示问题,并注意:您必须在fx应用程序线程上更新场景图中控件的属性(绝不能在后台线程中进行)。 - undefined
1个回答

11
请见下面的示例应用程序。 它提供了一个简单的进度条(ProgressBar)和一个标签(Label),用于演示如何使用后台任务(Task)的进度来更新UI界面。
此代码也有注释。
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ProgressBarExample extends Application {

    // Create our ProgressBar
    private ProgressBar progressBar = new ProgressBar(0.0);

    // Create a label to show current progress %
    private Label lblProgress = new Label();

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Button to start the background task
        Button button = new Button("Start");
        button.setOnAction(event -> startProcess());

        // Add our controls to the scene
        root.getChildren().addAll(
                progressBar,
                new HBox(5) {{
                    setAlignment(Pos.CENTER);
                    getChildren().addAll(
                            new Label("Current Step:"),
                            lblProgress
                    );
                }},
                button
        );

        // Here we will

        // Show the Stage
        primaryStage.setWidth(300);
        primaryStage.setHeight(300);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private void startProcess() {

        // Create a background Task
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                // Set the total number of steps in our process
                int steps = 1000;

                // Simulate a long running task
                for (int i = 0; i < steps; i++) {

                    Thread.sleep(10); // Pause briefly

                    // Update our progress and message properties
                    updateProgress(i, steps);
                    updateMessage(String.valueOf(i));
                }
                return null;
            }
        };

        // This method allows us to handle any Exceptions thrown by the task
        task.setOnFailed(wse -> {
            wse.getSource().getException().printStackTrace();
        });

        // If the task completed successfully, perform other updates here
        task.setOnSucceeded(wse -> {
            System.out.println("Done!");
        });

        // Before starting our task, we need to bind our UI values to the properties on the task
        progressBar.progressProperty().bind(task.progressProperty());
        lblProgress.textProperty().bind(task.messageProperty());

        // Now, start the task on a background thread
        new Thread(task).start();
    }
}

编辑: 添加了 setOnFailed()setOnSucceeded() 方法。


1
这解决了我的一个问题,但另一个问题还没有解决。最后一行仍然显示“总物品数量:0”。它不等待所有线程停止,而是立即继续执行代码。 - undefined
我通过使用这篇帖子找到了解决方法,只需使用task.setOnSucceeded方法插入我的最后一行代码。 - undefined
@KaranBijani - 谢谢你;我在这个答案中添加了额外的方法。 - undefined
非常有用对我来说 - undefined
那是不是说只有循环才能使用这个功能?如果我没有循环而是需要在服务器上执行多个命令,该怎么办? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接