如何在Java FX中的initialize()方法之前让控制器访问主应用程序?

时间:2018-06-12 08:02:33

标签: java javafx nullpointerexception

在我的控制器类中,我想在intialize()方法中使用一个对象。此对象(及其getter)在Main类中定义。出于这个原因,当我让控制器访问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 (和 Fabian )的解决方案,但我不明白你在做什么,为什么我要通过this到Controller类的构造函数。提前致谢

4 个答案:

答案 0 :(得分:3)

initialize()我假设您的意思是控制器中的initialize()方法,而不是init()课程中的Application方法(请告诉我,如果我和#39;我错了)。有两种方法可以做到这一点:

  1. 不要在FXML文件中使用fx:controller。而是,创建自己的实例并进行配置。然后在致电loader.setController(controller)之前致电load()

  2. 通过setControllerFactory(Callback)使用控制器工厂。在这里,您仍然可以使用fx:controller,并且可以将this实际传递到控制器类的构造函数中。

  3. 选项#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();
    

    如果您知道只有if将由控制器工厂实例化,您可以删除else支票以及YourController内的所有内容

    编辑:选项#2的替代示例

    fabian的评论中提到了这一点。我在这里发帖是因为在答案中阅读代码比在评论中阅读代码要容易得多。

    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可以是某个接口或抽象类。这样可以避免检查需要特殊处理的每种类型。

    并且,再次由fabian提及,您可以通过引入依赖注入框架(如AfterburnerFX或CDI(上下文和依赖注入))来进一步实现这一点。

答案 1 :(得分:2)

要执行此操作,您可以创建控制器的手动实例,以在其中注入所需的对象。然后,您只需提交FXMLLoader

的实例

但是,要执行此操作,您必须从fx:controller文件中删除FXML声明

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();
    }
}

答案 2 :(得分:1)

另一个解决方案可能是以下一个(即使之前的两个解决方案也有效)。从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。

答案 3 :(得分:0)

我有类似的问题,但是我发现我的Controller空链接到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;
}