Как предоставить контроллеру доступ к основному приложению перед методом initialize () в Java FX? - PullRequest
0 голосов
/ 12 июня 2018

В моем классе контроллера я хочу использовать объект внутри метода intialize().Этот объект (и его получатель) определены в классе 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 , и оно работает хорошо.Однако мой setMainApp метод в классе Controller был таким:

public void setMainApp(MainApp mainApp) {
    this.mainApp = mainApp;

    // Add observable list data to the table
    configurationTable.setItems(mainApp.getConfigurationData());
}

Но мне пришлось поместить функцию в , чтобы добавить данные наблюдаемого списка в таблицу где-то еще.Правильно ли я поступил?

Кроме того, я хочу попробовать также попробовать решение Slaw Fabian ), но я не понял, чтоВы на самом деле делаете, и почему я должен передать this в конструктор моего класса контроллера.Заранее спасибо

Ответы [ 4 ]

0 голосов
/ 16 июля 2018

У меня была похожая проблема, но я обнаружил, что мой Контроллер имеет нулевую ссылку на 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;
}
0 голосов
/ 12 июня 2018

Для этого вы можете создать ручной экземпляр контроллера, в который можно вводить нужные вам объекты.Затем вы просто отправляете экземпляр 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();
    }
}
0 голосов
/ 12 июня 2018

Другим решением может быть следующее (даже если два предыдущих также работают).Удалите fx:controller из вашего файла FMXL и загрузите его следующим образом:

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

}

Редактировать

На самом деле это решение № 1 из ответа @ slaw .

0 голосов
/ 12 июня 2018

Под initialize() Я предполагаю, что вы имеете в виду метод initialize() в контроллере, а не метод init() в вашем классе Application (дайте мне знать, если я ошибаюсь).Это можно сделать двумя способами:

  1. Не использовать fx:controller в файле FXML.Скорее создайте свой собственный экземпляр и настройте его.Затем вы звоните loader.setController(controller) до и звоните load().

  2. Используйте фабрику контроллера через 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();

Возможно, вы сможете удалить проверку if, а также все, что находится внутри else, если вы знаете , что фабрикой контроллеров будет создан только YourController.

Изменить: Альтернативный пример для опции # 2

Это было упомянуто в комментариях Фабиана.Я публикую его здесь, потому что читать код в ответе намного проще, чем в комментарии.

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 (внедрение контекста и зависимости).).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...