FXML:"初始化后运行"

J-b*_*bob 3 java javafx

我有一个JavaFX应用程序,它使用FXML和用Java编写的控制器类.在Java控制器中,我需要注意不要在FXML Node元素上进行操作,直到它被初始化(否则我会得到一个NullPointerException),这在initialize方法运行之前是不可保证的.所以我发现自己做了很多这样的事情:

控制器在FXML文件中设置如下:

<Pane fx:controller="Controller" ...>
...
</Pane>
Run Code Online (Sandbox Code Playgroud)

然后这是Java文件中的控制器.

class Controller{
    @FXML
    Pane aPane;
    int globalValue;

    public void setSomething(int value){
        globalValue = value;
        if(!(aPane == null)){  //possibly null if node not initialized yet
            aPane.someMethod(globalValue)
        }
    }

    @FXML
    void initialize(){
        aPane.someMethod(globalValue) //guaranteed not null at this point
    }


}
Run Code Online (Sandbox Code Playgroud)

这有效,但它很笨重,也很重复.我必须创建globalValue属性,以防万一setSomething调用之前的方法initialize,我必须确保我的setSomething方法中的操作与中的操作相同initialize.

当然,有一种更优雅的方式来做到这一点.我知道JavaFX有一个Platform.runlater(...)方法可以保证在主应用程序线程上运行一些东西.Perhpas有类似的东西Platform.runAfterInitialize(...)等到初始化,或者如果初始化已经发生则立即运行?或者,如果有另一种方法,我愿意接受建议.

Jam*_*s_D 11

如果在FXML文件中指定控制器fx:controller="Controller",则在调用时FXMLLoader.load(...),FXMLLoader:

  1. 解析FXML文件
  2. Controller通过(有效地)调用其无参数构造函数创建一个实例(或者,在高级用法中,通过调用控制器工厂来设置它)
  3. 创建与FXML文件中的元素对应的UI元素
  4. 将任何元素注入fx:id到控制器实例中的匹配字段中
  5. 注册事件处理程序
  6. initalize()在控制器实例上调用(如果定义了这样的方法)
  7. 返回与FXML层次结构的根相对应的UI元素

只有在load()结束(即之后@FXML-annotated字段注入),你可以得到与控制器的参考loader.getController().因此,除了在控制器工厂实现中做一些非常不寻常的事情之外,您无法调用控制器实例上的任何方法,直到@FXML初始化了注入的字段.这里的空检查是多余的.


另一方面,如果您使用FXMLLoader.setController(...)初始化控制器,在这种情况下您不能使用fx:controller,您可以将值传递给构造函数.简单地避免set在将控制器传递给控制器​​之前调用控制器上的FXMLLoader方法,您可以假设任何@FXML注释字段在控制器的公共方法中初始化:

class Controller{
    @FXML
    Pane aPane;
    int globalValue;

    public Controller(int globalValue) {
        this.globalValue = globalValue ;
    }

    public Controller() {
        this(0);
    }

    public void setSomething(int value){
        globalValue = value;
        aPane.someMethod(globalValue)
    }

    @FXML
    void initialize(){
        aPane.someMethod(globalValue) //guaranteed not null at this point
    }


}
Run Code Online (Sandbox Code Playgroud)

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
Controller controller = new Controller(42);
loader.setController(controller);
Node root = loader.load();
Run Code Online (Sandbox Code Playgroud)