如何在 Java FX 中的 initialize() 方法之前让控制器访问主应用程序?
Posted
技术标签:
【中文标题】如何在 Java FX 中的 initialize() 方法之前让控制器访问主应用程序?【英文标题】:How to give the controller access to the main app before the initialize() method in Java FX? 【发布时间】:2018-11-21 13:16:34 【问题描述】:在我的控制器类中,我想在 intialize()
方法中使用一个对象。此对象(及其 getter)在 Main 类中定义。出于这个原因,当我让控制器访问主应用程序时已经太晚了,因为对象在没有被初始化的情况下被调用。这将导致NullPointerException
。
这是调用.fxml
及其控制器的方法:
public void showConfigurationOverview()
try
// Load configuration overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/ConfigurationOverview.fxml"));
AnchorPane configurationOverview = (AnchorPane) loader.load();
// Set configuration overview into the center of root layout.
rootLayout.setCenter(configurationOverview);
// Give the controller access to the main app.
ConfigurationOverviewController controller = loader.getController();
controller.setMainApp(this);
catch (IOException e)
e.printStackTrace();
如果无法提供此早期访问权限,我该如何找到解决方案?我应该在控制器类中做些什么吗?
编辑:
感谢您的回答。我已经尝试过 mr mcwolf 的解决方案,效果很好。然而我在 Controller 类中的setMainApp
方法是这样的:
public void setMainApp(MainApp mainApp)
this.mainApp = mainApp;
// Add observable list data to the table
configurationTable.setItems(mainApp.getConfigurationData());
但我必须将函数将可观察列表数据添加到表中其他地方。我做对了吗?
此外,我还想尝试Slaw(和F***)的解决方案,但我不明白你实际上在做什么以及我为什么要通过this
给我的控制器类的构造函数。提前致谢
【问题讨论】:
根据您的编辑编辑我的。 为了更通用,如果您使用@mrmcwolf 解决方案,只需在控制器ConfigurationOverviewController
的初始化方法中添加configurationTable.setItems(mainApp.getConfigurationData());
。
谢谢我也这样做了。然而,在@mrmcwolf 的解决方案中,现在我收到了一份来自Intellij 的报告,其中指出现在从未使用过initialize()
方法。这是正常的还是只是IDE的错误?因为该应用程序似乎运行良好。
你添加了@FXML注解吗?
是的,当然。你可以从这个img
【参考方案1】:
initialize()
我假设您指的是控制器中的initialize()
方法,而不是Application
类中的init()
方法(如果我弄错了,请告诉我)。有两种方法可以做到这一点:
不要在 FXML 文件中使用 fx:controller
。相反,创建您自己的实例并对其进行配置。然后你打电话给loader.setController(controller)
之前打电话给load()
。
通过setControllerFactory(Callback)
使用控制器工厂。在这里您仍然可以使用fx:controller
,并且实际上可以将this
传递到您的控制器类的构造函数中。
选项 #2 的示例:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(/* your location */);
loader.setControllerFactory(clazz ->
if (YourController.class.equals(clazz))
return new YourController(this);
else
try
return clazz.getConstructor().newInstance();
catch (ReflectiveOperationException ex)
throw new RuntimeException(ex); // bail
);
Parent root = loader.load();
如果您知道只有YourController
将被控制器工厂实例化,您也许可以删除if
检查以及else
中的所有内容。
编辑:选项#2的替代示例
f*** 在 cmets 中提到了这一点。我在这里发布它是因为阅读答案中的代码比阅读评论更容易。
loader.setControllerFactory(clazz ->
Object controller;
try
controller = clazz.getConstructor().newInstance();
catch (ReflectiveOperationException ex)
throw new RuntimeException(ex);
if (controller instanceof BaseController)
((BaseController) controller).setMainApp(this);
return controller;
);
BaseController
可能是某个接口或抽象类。这使您可以避免检查需要特殊处理的每种类型。
而且,f*** 再次提到,您可以通过引入像 AfterburnerFX 或 CDI(上下文和依赖注入)这样的依赖注入框架来更进一步。
【讨论】:
考虑所有需要特殊处理的类的替代方法,您可以使用接口或公共基类。这将允许您对需要访问应用程序类的多个类使用相同的逻辑。Object controller; try controller = clazz.newInstance(); (ReflectiveOperationException ex) throw new RuntimeException(ex); if (controller instanceof MyType) ((MyType) controller).setMain(this); return controller;
使用依赖注入框架可以更进一步...【参考方案2】:
为此,您可以手动创建控制器实例,在其中注入您需要的对象。然后你只需提交FXMLLoader
的实例。
但是,为此,您必须从 FXML
文件中删除 fx:controller
语句
public void showConfigurationOverview()
try
ConfigurationOverviewController controller = new ConfigurationOverviewController();
controller.setMainApp(this);
// Load configuration overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/ConfigurationOverview.fxml"));
loader.setController(controller);
AnchorPane configurationOverview = (AnchorPane) loader.load();
// Set configuration overview into the center of root layout.
rootLayout.setCenter(configurationOverview);
// Give the controller access to the main app.
//ConfigurationOverviewController controller = loader.getController();
//controller.setMainApp(this);
catch (IOException e)
e.printStackTrace();
【讨论】:
【参考方案3】:另一种解决方案可能是以下一种(即使前两种也有效)。从您的 FMXL 文件中删除 fx:controller
并按如下方式加载:
public void showConfigurationOverview()
// Give the controller access to the main app.
ConfigurationOverviewController controller = new ConfigurationOverviewController(this);
root.setCenter(controller.getConfigurationOverview());
然后像下面这样改变你的控制器:
public class ConfigurationOverviewController
private AnchorPane configurationOverview;
private MainApp mainApp;
@FXML
private TableView configurationTable;
public ConfigurationOverviewController(MainApp pMain)
mainApp = pMain;
FXMLLoader loader = new FXMLLoader(getClass().getResource("YOUR_FXML"));
loader.setController(this);
try
configurationOverview = (AnchorPane)loader.load();
catch (IOException e)
e.printStackTrace();
public AnchorPane getConfigurationOverview()
return configurationOverview;
@FXML
private void initialize()
// Do your stuff with mainApp
configurationTable.setItems(mainApp.getConfigurationData());
编辑
实际上是@slaw 's answer的解决方案#1。
【讨论】:
【参考方案4】:我遇到了类似的问题,但我发现我的 Controller nulls 链接到 MainApp。这就是我创建链接static
的原因。在此之后我没有得到任何NullPointerException
。
可能会有所帮助。
public class LoginController
@FXML
private Label passwordLabl;
@FXML
private TextField passwordTexF;
public static WordLernApp wordLernApp;
public LoginController()
LoginController.wordLernApp = null;
public void setWordLernApp(WordLernApp wordLernApp)
LoginController.wordLernApp = wordLernApp;
【讨论】:
以上是关于如何在 Java FX 中的 initialize() 方法之前让控制器访问主应用程序?的主要内容,如果未能解决你的问题,请参考以下文章
Java FX 中的“mnemonicParsing”属性是啥
如何在android平台上部署java fx应用程序[重复]