JavaFX:在单独的非控制器类中更新UI
我有一个JavaFX程序,它启动一个由单独类执行的任务。我想将该任务的进度状态输出到UI中的JavaFX:在单独的非控制器类中更新UI,java,multithreading,user-interface,javafx,nullpointerexception,Java,Multithreading,User Interface,Javafx,Nullpointerexception,我有一个JavaFX程序,它启动一个由单独类执行的任务。我想将该任务的进度状态输出到UI中的标签元素。我不能让它工作。这是我的密码: Main.java: public class Main extends Application { public void start(Stage primaryStage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("ui.fxml")
标签元素。我不能让它工作。这是我的密码:
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);
}
}
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();
}
}
Controller.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);
}
}
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();
}
}
我要更新的标签是由delcaring Global@FXML public label label=new label()创建的代码>。通过newthread(newtask(){…})创建一个新线程
从TaskRun
运行collect_data
方法。TaskRun
类说明如下:
TaskRun.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);
}
}
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();
}
}
类任务运行{
专用控制器用户界面;
TaskRun(){
FXMLLoader=newFXMLLoader(getClass().getResource(“ui.fxml”);
ui=loader.getController();
}
无效收集_数据(){
对于(int i=0;i<100;i++){
//做些事情。。。
将进度发送到用户界面(((浮动)i/(浮动))*100;
}
}
无效发送进度到用户界面(浮动百分比){
新线程(新任务(){
@凌驾
受保护的Void调用()引发异常{
Platform.runLater(()->ui.label.setText(Float.toString(完成百分比)+“%”);
返回null;
}
}).start();
}
}
我在平台的线路上得到一个NullPointerException
。runLater(…)
所以很明显,这种方法是行不通的。如何通过这个非控制器类更新UITaskRun
?传递更新UI的控制器实例解决了这个问题(感谢转到@azro)。事实证明,使用fxmlLoader.getController()
实例化控制器不会返回正在更新UI的控制器的当前实例
所有需要修改的是TaskRun
构造函数:
TaskRun(Controller c) {
ui = c;
}
其中,ui
是TaskRun
类的全局Controller
对象
控制器类在线程中实例化TaskRun
对象,如下所示:TaskRun task=newtaskrun(controller.this)代码>
进行这些更改后,UI将按预期进行更新。传递更新UI的控制器实例可以解决问题(感谢转到@azro)。事实证明,使用fxmlLoader.getController()
实例化控制器不会返回正在更新UI的控制器的当前实例
所有需要修改的是TaskRun
构造函数:
TaskRun(Controller c) {
ui = c;
}
其中,ui
是TaskRun
类的全局Controller
对象
控制器类在线程中实例化TaskRun
对象,如下所示:TaskRun task=newtaskrun(controller.this)代码>
进行这些更改后,UI将按预期进行更新。您的设计对您在此处尝试执行的操作没有太大帮助。类应该实现任务,而不是完全隐藏任务并创建线程来运行它(这实际上是不灵活的:如果您想将任务提交给现有的执行器怎么办?)。然后,您可以只更新任务的进度,这是一个可观察的属性:
class TaskRun extends Task<Void> {
@Override
protected Void call() {
for (int i = 0; i < 100; i++) {
// do stuff...
updateProgress(i, 100);
}
return null ;
}
}
或者,使用绑定而不是侦听器:
label.textProperty().bind(task.progressProperty().asString("%.2f percent complete"));
这消除了所有可怕的耦合,因为现在TaskRun
不需要了解任何有关控制器类的信息。您的设计对您在这里尝试的工作没有太大帮助。类应该实现任务,而不是完全隐藏任务并创建线程来运行它(这实际上是不灵活的:如果您想将任务提交给现有的执行器怎么办?)。然后,您可以只更新任务的进度,这是一个可观察的属性:
class TaskRun extends Task<Void> {
@Override
protected Void call() {
for (int i = 0; i < 100; i++) {
// do stuff...
updateProgress(i, 100);
}
return null ;
}
}
或者,使用绑定而不是侦听器:
label.textProperty().bind(task.progressProperty().asString("%.2f percent complete"));
这消除了所有可怕的耦合,因为现在TaskRun
不需要了解任何关于控制器类的信息。如果是控制器启动了TaskRun实例,可以这样做:newtaskrun(This),在TaskRun的构造函数中:TaskRun(controller control){ui=contro;)可能像这样尝试;),但这取决于Taskrun是在控制器内的线程中创建的。也许这就是问题所在?不确定,但我肯定不会像您那样工作,因为您将添加2个控制器实例,它必须是相同的,尝试我编写的内容并告诉usCan您至少告诉我们什么是null以及为什么您希望它不为空?但一般来说,对链接到从未实际显示的UI的控制器执行某些操作不会取得任何效果,即使您排除了所有异常。单独创建FXMLLoader
不会导致加载fxml,从而创建控制器。这意味着UI
为null
。如果是控制器启动Taskrun实例,则可以执行以下操作:新建Taskrun(this),并且在Taskrun的构造函数中:Taskrun(控制器控件){ui=contro;)可能像这样尝试;),但这取决于Taskrun是在控制器内的线程中创建的。也许这就是问题所在?不确定,但我肯定不会像您那样工作,因为您将添加2个控制器实例,它必须是相同的,尝试我编写的内容并告诉usCan您至少告诉我们什么是null以及为什么您希望它不为空?但一般来说,对链接到从未实际显示的UI的控制器执行某些操作不会取得任何效果,即使您排除了所有异常。单独创建FXMLLoader
不会导致加载fxml,从而创建控制器。这意味着UI
isnull
.I ag