首页 > 解决方案 > JavaFX 更新进度条并等待线程完成

问题描述

我正在尝试更新 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 也发生了同样的事情。我不知道现在该怎么办。

标签: javamultithreadinguser-interfacejavafx

解决方案


请参阅下面的示例应用程序。它提供了一个简单的ProgressBar和一个Label来演示如何使用背景进度更新 UI Task

代码也被注释掉了。

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()andsetOnSucceeded()方法。


推荐阅读