我遇到了一个设计问题,它与事件处理和JavaFX控制器初始化的顺序有关 .
每当选择相应的选项卡时,我想更新TabPane . 为此,我使用FXML注册事件处理程序,如下所示:
<Tab fx:id="browseCollectionTab" onSelectionChanged="#tabChanged" text="Browse Images">
在事件处理代码中,我得到了类似的东西
@FXML
private void tabChanged() throws IOException{
if(browseCollectionTab.isSelected())
updateImageView();
}
updateImageView依次使用依赖注入传递给控制器的数据源加载图像 .
Option 1: 此依赖注入目前实现如下:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
Parent root = fxmlLoader.load();
AbstractController ctrl = (AbstractController)fxmlLoader.getController();
ctrl.setModel(this.model);
ctrl.setUp();
Option 2 我可以使用控制器的initialize()方法使用单例初始化它 . 这确实打破了依赖注入,并不是我的首选解决方案 .
Option 3 我可以避免使用FXML并手动实例化所有内容 . 这允许我在调用JavaFX / FXML之前实例化控制器并执行依赖注入 . 网上有很多例子,对于复杂的GUI来说都是一团糟 . 我想坚持FXMLLoader因为这看起来像一个整洁舒适的方式 . 如果这实际上不是最佳做法,请指出 .
Option 4 我可以在控制器的initialize()方法中手动注册事件处理程序(或者就此而言,在执行依赖注入/从其他地方设置控制器之后) . 这无疑首先在FXML中定义事件处理程序 .
那么,选项1和2有什么问题? tabChanged实际上是在控制器上执行任何初始化之前调用的,导致空指针异常 . 现在,我可以在控制器初始化之前忽略所有事件 - 这可能是一个坏主意,因为只会出现一次出现的事件 . 另一种选择是在(可能)许多事件处理程序中强制执行初始化 . 这似乎也不是一个可行的选择 .
我必须遗漏一些明显的东西 . 我知道这与常见的设计选择/最佳实践有关;但是,我无法向Google提供正确的关键字 .
我期待着您的帮助/建议 - 谢谢!
1 回答
您显示的示例实际上是一个非常不寻常的示例:通常在加载过程完成之前无法调用事件处理程序 . 选项卡选择是一种异常,因为您实际上是在响应可能以编程方式发生的属性更改,并且确实会在将选项卡添加到空选项卡窗格时发生 . 所以这是一个不寻常的情况,可以在加载完成之前调用事件处理程序 .
简单的解决方案
考虑更改将控制器与FXML文件关联的方式 . 一个选项是从FXML文件中 remove the fx:controller attribute ,并在代码中设置控制器 . 这使您有机会首先正确初始化控制器:
更复杂的方法
另一个更复杂的选择是使用控制器工厂 . 这是一个将控制器类映射到实际控制器实例的函数 . 在这种情况下,你仍然在FXML文件中有
fx:controller
属性(并且这种创建FXML文件的标准方式可以被认为是一个好处,因为它为SceneBuilder提供了检查方法和@FXML
注释字段等的机会) . 另一个好处是控制器工厂传播到<fx:include>
附带的任何FXML文件,这允许您在使用它们之前初始化它们的控制器 .在下面我假设您的
model
是Model
类型:首先,将控制器定义为具有将
Model
作为参数的构造函数,即 .要创建可重用的控制器工厂,您需要一些反思:
然后你就做了
此技术还允许您使用依赖注入框架 . 例如 . 如果你使用Spring,你可以这样做
现在,您的控制器实例将由Spring bean工厂创建和管理,您可以使用Spring依赖注入将模型注入其中 .