FXML:"在初始化后运行"

时间:2016-10-25 18:07:49

标签: java javafx

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

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

<Pane fx:controller="Controller" ...>
...
</Pane>

然后是 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属性,以防在调用setSomething之前调用initialize方法,并且我必须确保setSomething方法中的操作是与initialize中的操作相同。

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

1 个答案:

答案 0 :(得分:10)

如果您使用fx:controller="Controller"在FXML文件中指定控制器,那么当您致电FXMLLoader.load(...)时,FXMLLoader

  1. 解析FXML文件
  2. 通过(有效地)调用其无参数构造函数来创建Controller的实例(或者,在高级用法中,通过调用控制器工厂来设置它)
  3. 创建与FXML文件中的元素相对应的UI元素
  4. fx:id的所有元素注入控制器实例中的匹配字段
  5. 注册事件处理程序
  6. 在控制器实例上调用initalize()(如果定义了这样的方法)
  7. 返回与FXML层次结构的根对应的UI元素
  8. 只有在load()完成后(即 注入@FXML - 注释字段后)才能获得对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
        }
    
    
    }
    

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