我有一个JavaFX程序,可以启动由单独的类执行的任务。我想将该任务的进度状态输出到UI中的Label元素。我无法使它正常工作。这是我的代码:

Main.java:

public class Main extends Application {
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("ui.fxml"));
        primaryStage.setTitle("Data collector");
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }

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


Controller.java:

我要更新的标签是通过全局全局替换@FXML public Label label = new Label();创建的。通过new Thread(new Task<Void>() { ... }).start();创建一个新线程以运行collect_data中的TaskRun方法。 TaskRun类如下所示:

TaskRun.java:

class TaskRun {
    private Controller ui;

    TaskRun() {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("ui.fxml"));
        ui = loader.getController();
    }

    void collect_data() {
        for (int i = 0; i < 100; i++) {
            // do stuff...
            send_progress_to_ui(((float) i / (float)) * 100);
        }
    }

    void send_progress_to_ui(float percent) {
        new Thread(new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(() -> ui.label.setText(Float.toString(percent_complete) + "%"));

                return null;
            }
        }).start();
    }

}


我在NullPointerException的行上得到一个Platform.runLater(...)

所以很明显,这种方法行不通。如何通过此非控制器类TaskRun更新UI?

最佳答案

您的设计对于您在这里所做的工作并不是很有帮助。您的类应该实现该任务,而不是一个类来完全隐藏该任务并创建一个线程来运行它(这确实是不灵活的:如果您想将任务提交到现有的Executor怎么办?”)。然后,您可以仅更新任务的进度,这是一个可观察的属性:

class TaskRun extends Task<Void> {

    @Override
    protected Void call() {
        for (int i = 0; i < 100; i++) {
            // do stuff...
            updateProgress(i, 100);
        }
        return null ;
    }

}


现在,您可以在控制器中执行以下操作:

TaskRun task = new TaskRun();
task.progressProperty().addListener((obs, oldProgress, newProgress) ->
    label.setText(String.format("%.2f percent complete", newProgress.doubleValue()*100)));
new Thread(task).start();


或者,使用绑定而不是侦听器:

label.textProperty().bind(task.progressProperty().asString("%.2f percent complete"));


这消除了您所有的可怕耦合,因为现在TaskRun无需了解控制器类的任何知识。

09-25 20:51