FXML:“初始化后运行”

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

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

  ...  

然后这是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 } } 

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

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

如果使用fx:controller="Controller"在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字段之后 ),才能使用loader.getController()获得对控制器的引用。 因此,除了在控制器工厂实现中做一些非常不寻常的事情之外,您无法调用控制器实例上的任何方法,直到初始化@FXML字段之后。 这里的空检查是多余的。


另一方面,如果您使用FXMLLoader.setController(...)来初始化控制器,在这种情况下您不能使用fx:controller ,您可以将值传递给构造函数。 在将控制器传递给FXMLLoader之前,简单地避免在控制器上调用set方法意味着您可以假设在控制器的公共方法中初始化了任何@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 } } 

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