Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaFX:在单独的非控制器类中更新UI_Java_Multithreading_User Interface_Javafx_Nullpointerexception - Fatal编程技术网

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")

我有一个JavaFX程序,它启动一个由单独类执行的任务。我想将该任务的进度状态输出到UI中的
标签
元素。我不能让它工作。这是我的密码:

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(…)


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

传递更新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
is
null
.I ag