如何在 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”属性是啥

java - 如何在java fx中每2秒更新一次标签框?

如何在android平台上部署java fx应用程序[重复]

如何强制 Java FX 场景刷新?

如何在 Ubuntu 18.04.2 LTS 上使用 OpenJDK 8 运行 Java FX?

想法错误的场景构建器exe [重复]上的Java FX场景生成器