전달 매개 변수 JavaFX FXML

javafx parameters dependency-injection parameter-passing 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 는 새 창입니다. 문제는 컨트롤러에 고객 정보를 어디에서 찾을 것인지 알려주는 방법을 찾을 수 없다는 것입니다 (id를 매개 변수로 전달하여).

어떤 아이디어?




Answer 1 jewelsea


권장 접근법

이 답변은 FXML 컨트롤러에 매개 변수를 전달하기위한 다양한 메커니즘을 열거합니다.

소규모 응용 프로그램의 경우 호출자에서 컨트롤러로 직접 매개 변수를 전달하는 것이 좋습니다. 간단하고 간단하며 추가 프레임 워크가 필요하지 않습니다.

더 크고 복잡한 응용 프로그램의 경우 응용 프로그램 내에서 Dependency Injection 또는 Event Bus 메커니즘 을 사용하려는 경우 조사하는 것이 좋습니다 .

발신자에서 컨트롤러로 직접 매개 변수 전달

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이며 다음과 같은 방법으로 FXML 리소스에서 이러한 URL을 생성 할 수 있습니다.

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

FXMLLoader에서 정적로드 기능을 사용하지 않도록주의하십시오. 그렇지 않으면 로더 인스턴스에서 컨트롤러를 가져올 수 없습니다.

FXMLLoader 인스턴스 자체는 도메인 개체에 대해 전혀 알지 못합니다. 대신 응용 프로그램 특정 도메인 개체를 FXMLLoader 생성자로 직접 전달하지 않습니다.

  1. 지정된 위치에서 fxml 마크 업을 기반으로 FXMLLoader 생성
  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();

호출자에서 원하는 매개 변수를 컨트롤러 생성자로 전달하여 코드로 새 컨트롤러를 구성 할 수 있습니다. 컨트롤러를 구성한 후에 는 load() 인스턴스 메서드 를 호출 하기 전에 FXMLLoader 인스턴스에서 컨트롤러를 설정할 수 있습니다 .

로더 (JavaFX 2.x)에서 컨트롤러를 설정하기 위해 fxml 파일에서 fx:controller 속성을 정의 할 수도 없습니다 .

FXML 의 fx:controller 정의에 대한 제한으로 인해 개인적으로 컨트롤러를 FXMLLoader로 설정하는 대신 FXMLLoader에서 컨트롤러를 얻는 것을 선호합니다.

컨트롤러가 외부 정적 메소드에서 매개 변수를 검색하도록하기

이 메소드는 Controller.java 파일의 Javafx 2.0 How-to Application.getParameters ()에 대한 Sergey의 답변으로 예시됩니다 .

의존성 주입 사용

FXMLLoader는 Guice, Spring 또는 Java EE CDI와 같은 종속성 주입 시스템을 지원하여 FXMLLoader에서 사용자 정의 컨트롤러 팩토리를 설정할 수 있습니다. 이는 각각의 의존성 주입 시스템에 의해 주입 된 의존성 값으로 컨트롤러 인스턴스를 생성하는 데 사용할 수있는 콜백을 제공합니다.

Spring을 사용한 JavaFX 애플리케이션 및 컨트롤러 종속성 주입의 예는 다음에 대한 답변으로 제공됩니다.

정말로 멋진 깨끗한 의존성 주입 방식이 예시된다 afterburner.fx 워크 시료와 공기 해킹 어플리케이션 용도 것이있다. afterburner.fx는 JEE6 javax.inject 를 사용하여 종속성 주입을 수행합니다.

이벤트 버스 이용

원래 FXML 사양 작성자이자 구현자인 Greg Brown 은 FXML 인스턴스화 된 컨트롤러와 다른 응용 프로그램 논리 간의 통신을 위해 Guava EventBus 와 같은 이벤트 버스 사용을 고려할 것을 제안 합니다.

EventBus는 POJO가 서로 참조하지 않고도 JVM의 어느 곳에서나 서로 통신 할 수 있도록하는 주석이있는 단순하지만 강력한 발행 / 구독 API입니다.

후속 Q & A

첫 번째 방법에서는 왜 Stage를 반환합니까? show (); 명령을 이미 제공했기 때문에이 메소드도 무효가 될 수 있습니다. 귀국 바로 전에;. 스테이지를 반환하여 사용을 계획하는 방법

문제에 대한 기능적 솔루션입니다. showCustomerDialog 함수 에서 스테이지가 반환 되어 나중에 기본 창에서 버튼 클릭을 기반으로 스테이지를 숨기는 등의 작업을 수행하려는 외부 클래스에서 참조를 저장할 수 있습니다. 대체 객체 지향 솔루션은 CustomerDialog 객체 내부의 기능 및 스테이지 참조를 캡슐화하거나 CustomerDialog extend Stage를 가질 수 있습니다. FXML, 컨트롤러 및 모델 데이터를 캡슐화하는 사용자 정의 대화 상자에 대한 객체 지향 인터페이스의 전체 예는이 답변의 범위를 벗어 났지만 누구나 블로그 게시물을 작성하려는 경향이 있습니다.


@dzim 이라는 StackOverflow 사용자가 제공 한 추가 정보

스프링 부트 의존성 주입 예제

"The Spring Boot Way"를 수행하는 방법에 대한 질문에 JavaFX 2에 대한 토론이있었습니다. 이 접근 방식은 2016 년 3 월 Spring Boot v1.3.3에서 여전히 유효하고 테스트되었습니다 .RELEASE : https://stackoverflow.com/a/36310391/1281217


때로는 결과를 발신자에게 다시 전달할 수도 있습니다.이 경우 관련 질문에 대한 답변을 확인할 수 있습니다.




Answer 2 Zephyr


나는 이것이 매우 오래된 게시물이라는 것을 알고 있으며 이미 훌륭한 답변을 얻었지만 그러한 접근법 중 하나를 보여주기 위해 간단한 MCVE를 만들고 새로운 코더가 개념을 실제로 볼 수있는 방법을 원했습니다.

이 예에서는 5 개의 파일을 사용합니다.

  1. Main.java- 응용 프로그램을 시작하고 첫 번째 컨트롤러를 호출하는 데 사용됩니다.
  2. Controller1.java- 첫 번째 FXML 레이아웃의 컨트롤러입니다.
  3. Controller2.java- 두 번째 FXML 레이아웃의 컨트롤러입니다.
  4. Layout1.fxml- 첫 번째 장면의 FXML 레이아웃입니다.
  5. Layout2.fxml- 두 번째 장면의 FXML 레이아웃입니다.

모든 파일은이 게시물의 맨 아래에 전체적으로 나열됩니다.

목표 : Controller1 에서 Controller2 로 또는 그 반대로 값을 전달하는 것을 보여줍니다 .

프로그램 흐름 :

  • 첫 번째 장면에는 TextField , ButtonLabel 이 있습니다. 때 Button 클릭, 두 번째 창이로드되고에 입력 한 텍스트를 포함하여 표시 TextField .
  • 두 번째 장면에는 TextField , ButtonLabel 도 있습니다. Label 에 입력 한 텍스트가 표시됩니다 TextField 첫 번째 장면에있다.
  • 두 번째 장면의 TextField 에 텍스트를 입력 하고 Button 을 클릭하면 첫 번째 장면의 Label 이 업데이트되어 입력 한 텍스트가 표시됩니다.

이것은 매우 간단한 시연이며 확실히 개선의 여지가 있지만 개념을 매우 명확하게 만들어야합니다.

또한 코드 자체에는 무슨 일이 일어나고 있는지에 대한 몇 가지 세부 사항이 설명되어 있습니다.

코드

Main.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>



Answer 3 Alexander Kirov


javafx.scene.Node 클래스에는 setUserData (Object) 및 Object getUserData () 메소드 쌍이 있습니다.

노드에 정보를 추가하는 데 사용할 수 있습니다.

따라서 page.setUserData (info);를 호출 할 수 있습니다.

정보가 설정되어 있으면 컨트롤러가 확인할 수 있습니다. 또한 필요한 경우 뒤로 전달 데이터 전송에 ObjectProperty를 사용할 수 있습니다.

다음 문서를 참조 하십시오. http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html "첫 번째 버전에서 handleButtonAction ()은 @FXML로 태그 지정됩니다. 컨트롤러의 문서에 정의 된 마크 업이이를 호출 할 수 있도록합니다. 두 번째 예에서는 버튼 필드에 주석을 달아 로더가 값을 설정할 수 있도록합니다. initialize () 메소드도 비슷하게 주석이 달립니다. "

따라서 제어기를 노드와 연관시키고 사용자 데이터를 노드로 설정해야합니다.




Answer 4 user1503636


다음은 네임 스페이스를 통해 매개 변수를 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>

네임 스페이스 변수 labelText 에 대한 External Text 값을 정의하십시오 .

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



Answer 5 diego matos - keke


이 작동합니다 ..

전달하는 값을 처음 인쇄 할 때 null이된다는 것을 기억하십시오. 창을로드 한 후에는 다른 구성 요소에 대해 코딩하려는 모든 항목에 동일하게 사용할 수 있습니다.

첫 컨트롤러

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