Передача параметров JavaFX FXML


как я могу передать параметры во вторичное окно в javafx? Есть ли способ связаться с соответствующим контроллером?

например: Пользователь выбирает клиента из TableView и откроется новое окно с информацией о клиенте.

Stage newStage = new Stage();
try 
{
    AnchorPane page = (AnchorPane) FXMLLoader.load(HectorGestion.class.getResource(fxmlResource));
    Scene scene = new Scene(page);
    newStage.setScene(scene);
    newStage.setTitle(windowTitle);
    newStage.setResizable(isResizable);
    if(showRightAway) 
    {
        newStage.show();
    }
}

newStage будет новое окно. Проблема в том, что я не могу найти способ сообщить контроллеру, где искать информацию о клиенте (передавая идентификатор в качестве параметра).

какие идеи?

8 154

8 ответов:

Рекомендуемый Подход

в этом ответе перечислены различные механизмы передачи параметров контроллерам FXML.

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

для больших, более сложных приложений, было бы целесообразно исследовать, если вы хотите использовать Инъекции Зависимостей или Автобусная Событие механизмы в вашем приложении.

передача параметров непосредственно от вызывающего абонента к контроллеру

передайте пользовательские данные контроллеру FXML, извлекая контроллер из экземпляра загрузчика FXML и вызывая метод на контроллере, чтобы инициализировать его с требуемыми значениями данных.

что-то вроде следующего кода:

public Stage showCustomerDialog(Customer customer) {
  FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
      "customerDialog.fxml"
    )
  );

  Stage stage = new Stage(StageStyle.DECORATED);
  stage.setScene(
    new Scene(
      (Pane) loader.load()
    )
  );

  CustomerDialogController controller = 
    loader.<CustomerDialogController>getController();
  controller.initData(customer);

  stage.show();

  return stage;
}

...

class CustomerDialogController {
  @FXML private Label customerName;
  void initialize() {}
  void initData(Customer customer) {
    customerName.setText(customer.getName());
  }
}

новый FXMLLoader построен как показано на рисунке пример кода, т. е. new FXMLLoader(location). Расположение-это URL-адрес, и вы можете создать такой URL-адрес из ресурса FXML:

new FXMLLoader(getClass().getResource("sample.fxml"));

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

экземпляры FXMLLoader сами никогда ничего не знают об объектах домена. Вместо этого вы не передаете непосредственно объекты домена приложения в конструктор FXMLLoader ты:

  1. построить FXMLLoader на основе разметки fxml в указанном месте
  2. получить контроллер из экземпляра FXMLLoader.
  3. вызовите методы на извлеченном контроллере, чтобы предоставить контроллеру ссылки на объекты домена.

этот блог (другой писатель) предоставляет другой, но похожий, пример.

установка контроллера на FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = (Pane) loader.load();

вы можете построить новый контроллер в коде, передавая любые параметры, которые вы хотите от вызывающего абонента в конструктор контроллера. После того, как вы построили контроллер, вы можете установить его на экземпляр FXMLLoader до вызове load()экземпляр метод.

чтобы установить контроллер на загрузчике (в JavaFX 2.x) вы также не можете определить a fx:controller атрибут в вашем fxml файл.

из-за ограничения на fx:controller определение в FXML, я лично предпочитаю получать контроллер от FXMLLoader, а не устанавливать контроллер в FXMLLoader.

имея контроллер получить параметры из внешнего статического метода

этот метод иллюстрируется ответом Сергея на Javafx 2.0 практическое приложение.getParameters() в контроллере.Ява файл.

Использовать Инъекцию Зависимостей

FXMLLoader поддерживает системы впрыска зависимостей, такие как Guice, Spring или Java EE CDI, позволяя вам установить завод пользовательского контроллера на FXMLLoader. Это обеспечивает обратный вызов, который можно использовать для создания экземпляра контроллера с зависимыми значениями, введенными соответствующей системой внедрения зависимостей. Есть пример интеграции FXML-файл с весенним инъекции зависимостей система (к сожалению, ссылка мертва, и содержимое исчезло, если кто-нибудь знает о подобном примере, отредактируйте этот вопрос, чтобы ссылаться на него), хотя это немного неуклюже, чем при использовании новых функций фабрики пользовательских контроллеров, доступных в JavaFX 2.2.

действительно хороший, чистый подход к инъекции зависимостей иллюстрируется форсаже.база Форекс образец приложение air-hacks, который его использует. дожигатель.Форекс полагается на JEE6 javax.впрысните для выполнения инъекции зависимостей.

используйте шину событий

Грег Браун, создатель и исполнитель оригинальной спецификации FXML, часто предлагает рассмотреть возможность использования автобусная событие для связи между контроллерами FXML с экземплярами и другой логикой приложения.

EventBus-это простой, но мощный API публикации / подписки с аннотациями, который позволяет POJOs чтобы общаться друг с другом в любом месте в JVM без необходимости ссылаться друг на друга.

последующие вопросы и ответы

на первом методе, почему вы возвращаете этап? Метод также может быть пустым, потому что вы уже даете команду show(); непосредственно перед этапом возврата;. Как вы планируете использование, возвращая этап

это функциональное решение проблемы. Этап возвращается из

javafx.сцена.Класс Node имеет пару методов setUserData(объект) и Объект getUserData()

который вы могли бы использовать, чтобы добавить свою информацию к узлу.

Итак, вы можете позвонить на страницу.setUserData (info);

и контроллер может проверить, если информация имеет значение. Кроме того, вы можете использовать ObjectProperty для обратной передачи данных, если это необходимо.

соблюдайте документацию здесь: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html Перед фразой "в первой версии handleButtonAction () помечается с помощью @FXML, чтобы разрешить разметку, определенную в документе контроллера, чтобы вызвать его. Во втором примере поле кнопки аннотируется, чтобы позволить загрузчику установить его значение. Метод initialize() также Примеч."

Итак, вам нужно связать контроллер с узлом, и установить данных пользователя узел.

вот пример передачи параметров в документ fxml через пространство имен.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
    <BorderPane>
        <center>
            <Label text="$labelText"/>
        </center>
    </BorderPane>
</VBox>

определить значение External Text для переменной пространства имен labelText:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class NamespaceParameterExampleApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("namespace-parameter-example.fxml"));

        fxmlLoader.getNamespace()
                  .put("labelText", "External Text");

        final Parent root = fxmlLoader.load();

        primaryStage.setTitle("Namespace Parameter Example");
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }
}

это работает ..

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

Контроллер

try {
                                Stage st = new Stage();
                                 FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/inty360/free/form/MainOnline.fxml"));

                                Parent sceneMain = loader.load();

                                MainOnlineController controller = loader.<MainOnlineController>getController();
                                controller.initVariable(99L);

                                Scene scene = new Scene(sceneMain);
                                st.setScene(scene);
                                st.setMaximized(true);
                                st.setTitle("My App");
                                st.show();
                            } catch (IOException ex) {
                                Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
                            }

Другой Контроллер

public void initVariable(Long id_usuario){
        this.id_usuario = id_usuario;
        label_usuario_nombre.setText(id_usuario.toString());

    }

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

в этом примере мы будем использовать 5 файлов:

  1. Главная.java - просто используется для запуска приложения и вызова первого контроллера.
  2. Controller1.java - контроллера для первый макет FXML.
  3. Controller2.java - контроллер для второго макета FXML.
  4. Layout1.FXML-файл - макет FXML для первой сцены.
  5. Layout2.FXML-файл - макет FXML для второй сцены.

все файлы перечислены в полном объеме в нижней части этого сообщения.

Цель: для демонстрации передачи значений от Controller1 до Controller2 и наоборот.

Программа:

  • первая сцена содержит TextField, a Button и Label. Когда Button щелкается, второе окно загружается и отображается, включая текст, введенный в TextField.
  • во второй сцене также есть TextField, a Button и Label. Элемент Label отображает текст, введенный в TextField на первом сцена.
  • при вводе текста во второй сцене TextField и нажав Button первая сцена Label обновляется для отображения введенного текста.

это очень простая демонстрация и, безусловно, может стоять за некоторое улучшение, но должно сделать концепцию очень ясной.

сам код также прокомментирован с некоторыми деталями того, что происходит и как.

в Код

Главная.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.FXML-файл:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.FXML-файл:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>

вы должны создать один класс контекста.

public class Context {
    private final static Context instance = new Context();
    public static Context getInstance() {
        return instance;
    }

    private Connection con;
    public void setConnection(Connection con)
    {
        this.con=con;
    }
    public Connection getConnection() {
        return con;
    }

    private TabRoughController tabRough;
    public void setTabRough(TabRoughController tabRough) {
        this.tabRough=tabRough;
    }

    public TabRoughController getTabRough() {
        return tabRough;
    }
}

вам нужно просто установить экземпляр контроллера в инициализации с помощью

Context.getInstance().setTabRough(this);

и вы можете использовать его из всего приложения просто с помощью

TabRoughController cont=Context.getInstance().getTabRough();

Теперь вы можете передать параметр к любому регулятору от всего применения.

вот пример использования контроллера, введенного Guice.

/**
 * Loads a FXML file and injects its controller from the given Guice {@code Provider}
 */
public abstract class GuiceFxmlLoader {

   public GuiceFxmlLoader(Stage stage, Provider<?> provider) {
      mStage = Objects.requireNonNull(stage);
      mProvider = Objects.requireNonNull(provider);
   }

   /**
    * @return the FXML file name
    */
   public abstract String getFileName();

   /**
    * Load FXML, set its controller with given {@code Provider}, and add it to {@code Stage}.
    */
   public void loadView() {
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(getFileName()));
         loader.setControllerFactory(p -> mProvider.get());
         Node view = loader.load();
         setViewInStage(view);
      }
      catch (IOException ex) {
         LOGGER.error("Failed to load FXML: " + getFileName(), ex);
      }
   }

   private void setViewInStage(Node view) {
      BorderPane pane = (BorderPane)mStage.getScene().getRoot();
      pane.setCenter(view);
   }

   private static final Logger LOGGER = Logger.getLogger(GuiceFxmlLoader.class);

   private final Stage mStage;
   private final Provider<?> mProvider;
}

вот конкретная реализация загрузчика:

public class ConcreteViewLoader extends GuiceFxmlLoader {

   @Inject
   public ConcreteViewLoader(Stage stage, Provider<MyController> provider) {
      super(stage, provider);
   }

   @Override
   public String getFileName() {
      return "my_view.fxml";
   }
}

Примечание этот пример загружает представление в центр BoarderPane, который является корнем сцены в сцене. Это не имеет отношения к примеру (детали реализации моего конкретного случая использования), но решил оставить его, поскольку некоторые могут найти его полезным.

вы можете использовать общедоступный наблюдаемый список для хранения общедоступных данных или просто создать общедоступный метод setter для хранения данных и извлечения из соответствующего контроллера