我有一个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
无需了解控制器类的任何知识。