Как передать параметры конструктору контроллера при использовании ` `? - PullRequest
1 голос
/ 22 марта 2020

Я изучаю JavaFX, и я столкнулся с проблемой, связанной с созданием экземпляров контроллеров, которые я не могу решить. По сути, мне интересно, возможно ли выполнить одно из следующих действий:

  1. Передать параметр конструктору контроллера при включении F XML с <fx:include>; или
  2. Укажите пользовательский экземпляр контроллера, который будет использоваться при включении файла F XML с <fx:include>.

Обратите внимание, что эти проблемы связаны. Фактически, причина, по которой я спрашиваю о варианте (2), заключается в том, что он решит вариант (1).


Моя настройка


I иметь следующий «основной» файл F XML:

<!-- 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. Я не буду отображать его здесь, но могу при необходимости.

Возможно, вы заметили, что мой основной файл F XML не имеет атрибута fx:controller в его BorderPane, несмотря на то, что что я сказал, что у него есть связанный контроллер. Это потому, что вместо того, чтобы позволить FXMLLoader создать контроллер для меня (и, таким образом, я не могу передать параметры конструктору класса контроллера), я выбрал при загрузке главной страницы F XML в моем главном классе приложения (то есть классе, расширяющем 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();
}

Обратите внимание, что объект "менеджер проекта", определенный выше и переданный в конструктор контроллера главной панели, на самом деле является основной причиной этого весь вопрос; это (помимо передачи на главный контроллер) объект, который мне нужно передать контроллеру файла F XML, который я включил в основной файл F XML, используя <fx:include>.

Теперь, этот подход создания моего собственного экземпляра контроллера и передачи его FXMLLoader работает очень хорошо для меня. Это позволяет мне легко, без каких-либо размышлений, передавать параметры конструктору контроллера. Тем не менее, он работает только тогда, когда у меня есть объект FXMLoader, которому можно присвоить экземпляр контроллера.

В случае other , где я включаю файл F XML из основного F Файл XML с использованием <fx:include>, JavaFX создает для меня контроллер, не предоставляя мне возможности либо (1) передать параметры конструктору контроллера, либо (2) использовать мой собственный экземпляр контроллера.


Что я попробовал


Во время исследования этой проблемы я наткнулся на этот вопрос StackOverflow , который, по-видимому, имел какое-то отношение к проблеме , Из него я узнал о FXMLLoader.setControllerFactory(), который поначалу казался способным решить эту проблему. Однако, чтобы использовать его, я был вынужден использовать какое-то довольно грязное отражение, чтобы проверить, может ли конструктор типа принять мой объект, а затем использовать more отражение для создания контроллера, все время надеясь, что нет ошибки должны были быть брошены из-за лазейки в моем коде. Я был вынужден признать, что это не сработало.

Я также экспериментировал, вместо того, чтобы передать свой объект конструктору контроллера, установив объект на контроллере после контроллер был инициализирован. Однако это не сработало, потому что мне нужно было использовать объект в методе initialize() моего контроллера, который вызывается до . Я бы установил объект на контроллере. Потенциально это можно обойти, добавив другой метод инициализации , в котором может быть расположена любая функциональность, требующая объекта, например, objectInitialized(); но тогда мне нужно было бы добавить этот метод к каждому контроллеру, которому нужна эта функциональность, и я должен был бы не забыть вызвать все эти методы в какой-то момент. Кроме того, я хотел, чтобы объект был полем final в классе контроллера; очевидно, он не может быть окончательным, если его нужно установить извне.

Наконец, я также рассмотрел вариант, что для каждого файла F XML, который мне нужно включить в основной файл F XML, вместо включения его в FMXL, я мог бы сделать это из Java контроллер. Таким образом, я мог создать свой собственный FXMLLoader, установить на нем свой собственный экземпляр контроллера и, таким образом, решить проблему. Тем не менее, я бы, по возможности, предпочел бы сохранить весь код пользовательского интерфейса в файлах F XML.


Summary


In Итак, мне нужен способ передать параметры конструктору моего контроллера при использовании <fx:include>.

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

Спасибо всем за помощь!
—Jacob

1 Ответ

0 голосов
/ 23 марта 2020

После дальнейшего рассмотрения я решил просто реализовать вариант (3) в моем вопросе в разделе «Что я уже пробовал». По сути, вместо использования <fx:include> я решил, что было бы проще, более гибко и более перспективно включать любые контроллеры, которым требуются параметры напрямую / вручную из моего Java кода.


Почему?


Когда я больше думал о своей проблеме, я понял, что у меня абсолютно нет возможности передать параметр конструктору моего контроллера "из моего F XML». В конце концов, поскольку объект, который я пытался передать, был определен в моем коде Java (основной класс приложения), не было способа использовать его в F XML. Таким образом, вся моя точка зрения на проблему изменилась.

Вариант 1

После того, как я переоценил свои варианты, я все-таки решил просто использовать FXMLLoader.setControllerFactor(); этот подход был подтвержден James_D в комментариях, которые он разместил по моему вопросу: поскольку создание экземпляра контроллера all происходит посредством отражения, использование отражения для передачи объекта в контроллер не обязательно будет такой же плохой идеей, как и я. думал. На самом деле, это, вероятно, будет работать достаточно хорошо.

Однако, хотя это будет работать хорошо, если все, что мне нужно, это передать один объект контроллеру, что если, если для некоторых из мой контроллер, я хотел передать несколько объектов (и, возможно, разных типов)? Тогда фабрика контроллеров станет довольно громоздкой, поскольку мне придется проверять несколько параметров потенциально разных типов, а затем передавать правильные.

Кроме того, что если я захочу передать объект к контроллеру, не из прикладного класса , где определена фабрика контроллеров, а откуда-то еще? Например, представьте, что у меня есть класс приложения MyApplication.java, «главный» контроллер, MainController.java и контроллер «под» основным, NavigationBarController.java? Возможно, в какой-то момент мне захочется передать объект не от MyApplication до NavigationBarController, а от MainController до NavigationBarController. Если бы это было так (что вполне возможно), то моя фабрика контроллеров больше не работала бы, потому что у нее не было бы доступа к необходимому объекту. Таким образом, я пришел к выводу, что setControllerFactory() не будет работать для меня, по крайней мере, не очень хорошо.

Вариант 2 (что я использовал)

Другой вариант (и тот, который я в итоге использовал) должен был включать мои контроллеры из моего Java кода, используя FXMLLoader напрямую; и вместо того, чтобы включать все контроллеры из основного класса приложения, я мог бы включить каждый из них в контроллер , в котором он «проживал».

Например, используя пример, представленный в Option (1) выше, вместо того чтобы использовать фабрику контроллеров и атрибут fx:controller в файле F XML, я бы удалил атрибут fx:controller из любой пары файл / контроллер F XML, которой требовался доступ к моему объекту.

Вместо этого в MyApplication я бы использовал FXMLLoader напрямую для загрузки, инициализации и добавления MainController. Затем, в MainController, я бы использовал FXMLLoader для включения NavigationBarController.

Теперь, самым большим потенциальным недостатком этого подхода является тот факт, что мне нужно будет в основном повторять код загрузки каждый раз, когда я хотел включить пару файл / контроллер F XML. Чтобы решить эту проблему, я создал своего рода «служебный класс» под названием FXMLQuickLoader, который содержит методы для простой и быстрой загрузки пар файлов и контроллеров F XML, но с пользовательскими объектами контроллеров. Если вы заинтересованы в просмотре кода, я создал GitHub Gist , содержащий FXMLQuickLoader.java.

Вариант 3 (не проверено)

Сейчас, James_D указал, что я мог бы потенциально использовать нечто, называемое «фабрикой внедрения зависимостей». Если бы я его правильно понял, он бы автоматически инициализировал поля моих контроллеров, а не то, что мне нужно было передавать объекты в конструкторы. Похоже, это решение будет работать идеально; тем не менее, поскольку это мой первый проект JavaFX, и поскольку у меня есть некоторые временные ограничения, я решил, что, вероятно, потребуется слишком много времени, чтобы изучить его.

Однако для моего следующего проекта JavaFX я, вероятно, посмотрю в него и (настоятельно) рассмотреть возможность его использования. Я постараюсь обновить этот ответ позже, если я это сделаю.


Резюме


Итак, для себя я решил, что это будет лучше включить мои контроллеры вручную, используя FXMLLoader, вместо того, чтобы пытаться использовать одну из альтернатив. Тем не менее, я только изучаю JavaFX, и я не совсем разбираюсь в предмете, поэтому вы должны принять этот ответ с недоверием. Тем не менее, он может помочь другим пользователям и, по крайней мере, наметить некоторые потенциальные решения. Я также все еще открыт для дальнейших предложений по этому вопросу.

...