Я изучаю JavaFX и столкнулся с проблемой, связанной с созданием экземпляров контроллеров, которую я не могу решить. По сути, мне интересно, возможно ли сделать одно из следующего:
- Передать параметр конструктору контроллера при включении FXML с
<fx:include>
; или же - Укажите пользовательский экземпляр контроллера для использования при включении файла FXML с
<fx:include>
.
Обратите внимание, что эти вопросы связаны. На самом деле, причина, по которой я спрашиваю о варианте (2), заключается в том, что он решит вариант (1).
Моя установка
У меня есть следующий "основной" файл FXML:
<!-- XML declaration, imports, etc. removed for brevity -->
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml">
<!-- ... -->
<center>
<!-- Note that PageSwitcher is a custom control that is capable of switching between pages — you should be able to ignore it here. -->
<PageSwitcher fx:id="mainPageSwitcher" currentPageIndex="0">
<!-- ... -->
<fx:include source="dashboard.fxml" fx:id="dashboard" />
</PageSwitcher>
</center>
</BorderPane>
У него есть связанный контроллер, MainPaneController
. Здесь показывать не буду, но могу, если надо.
Вы могли заметить, что мой основной файл FXML не имеет атрибута fx:controller
вместо BorderPane
, несмотря на то, что я сказал, что у него есть связанный контроллер. Это связано с тем, что вместо того, чтобы позволить FXMLLoader
создать для меня контроллер (и, таким образом, лишив меня возможности передавать параметры конструктору класса контроллера), я выбрал при загрузке главной страницы FXML в моем основном классе приложения (т. е. класс, расширяющий Application), чтобы создать собственный экземпляр класса MainPaneController
. Вы можете увидеть метод start()
моего основного класса приложения:
@Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader mainPaneLoader;
MainPaneController mainPaneController;
Parent mainPane;
// Initialize the project manager.
projectManager = new ProjectManager(primaryStage);
// Initialize the main pane loader.
mainPaneLoader = new FXMLLoader();
// Initialize the main pane controller.
mainPaneController = new MainPaneController(projectManager);
// Load the main pane.
mainPaneLoader.setController(mainPaneController);
mainPaneLoader.setLocation(getClass().getResource(MAIN_PANE_FXML_PATH));
mainPane = mainPaneLoader.load();
Scene mainScene;
// Create the main scene and add it to the primary scene.
mainScene = new Scene(mainPane);
primaryStage.setScene(mainScene);
// Initialize the primary stage.
primaryStage.setTitle(APPLICATION_TITLE);
// Show the primary stage.
primaryStage.show();
}
Обратите внимание, что объект «менеджер проекта», определенный выше и переданный в конструктор контроллера главной панели, на самом деле является основной причиной всего этого вопроса; это (в дополнение к передаче основному контроллеру) объект, который мне нужно передать контроллеру файла FXML, который я включил в основной файл FXML, используя <fx:include>
.
Теперь этот подход создания моего собственного экземпляра контроллера и передачи его FXMLLoader
работает очень хорошо для меня. Это позволяет мне легко, без каких-либо запутанных размышлений, передавать параметры конструктору контроллера. Однако это работает только тогда, когда у меня есть объект FXMLoader
, которому я передаю экземпляр контроллера.
В другом случае, когда я включаю FXML-файл из основного FXML-файла с помощью <fx:include>
, JavaFX создает для меня контроллер, не предоставляя мне возможности (1) передать параметры конструктору контроллера, или (2) использовать мой собственный экземпляр контроллера.
Что я пробовал
Изучая эту проблему, я наткнулся на этот StackOverflow вопрос, который, казалось, имел хоть какое-то отношение к проблеме. Из него я узнал о FXMLLoader.setControllerFactory()
, который поначалу казался способным решить эту проблему. Однако, чтобы использовать его, я был вынужден использовать некоторое довольно запутанное отражение, чтобы проверить, может ли конструктор типа принять мой объект, а затем использовать отражение more для создания контроллера, все время надеясь, что никакие ошибки не будут выдаваться из-за лазейки в моем коде. Я был вынужден признать, что это не сработает.
Я также экспериментировал, вместо того чтобы передавать свой объект конструктору контроллера, устанавливая объект в контроллере после инициализации контроллера. Однако это не сработало, потому что мне нужно было использовать объект в методе initialize()
моего контроллера, который вызывается до того, как я устанавливаю объект в контроллере. Потенциально это можно обойти, добавив еще один метод инициализации, в котором можно найти любую функциональность, требующую объекта, возможно, с именем objectInitialized()
; но тогда мне пришлось бы добавить этот метод к каждому отдельному контроллеру, которому нужна эта функциональность, и я должен был бы не забыть вызвать все эти методы в какой-то момент. Кроме того, я хотел, чтобы объект был полем final
в классе контроллера; очевидно, он не может быть окончательным, если его нужно задавать извне.
Наконец, я также рассмотрел вариант, что для каждого файла FXML, который мне нужно включить в основной файл FXML, вместо того, чтобы включать его в FMXL, я мог бы сделать это из контроллера Java. Таким образом, я мог бы создать свой собственный FXMLLoader
, установить на нем свой собственный экземпляр контроллера и, таким образом, решить проблему. Однако я бы, если возможно, предпочел бы сохранить весь код пользовательского интерфейса в файлах FXML.
Сводка
Таким образом, мне нужен способ передать параметры конструктору моего контроллера при использовании <fx:include>
.
Я понимаю, что это длинный вопрос и несколько сложная проблема, поэтому я очень ценю любую помощь, которую вы можете предоставить. Кроме того, пожалуйста, дайте мне знать в комментариях, если мне нужно что-то уточнить или опубликовать дополнительный код.
Спасибо всем за помощь!
— Джейкоб