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를 매개 변수로 전달하여).
어떤 아이디어?
답변
권장 접근법
이 답변은 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 생성자로 직접 전달하지 않습니다.
- 지정된 위치에서 fxml 마크 업을 기반으로 FXMLLoader 생성
- FXMLLoader 인스턴스에서 컨트롤러를 가져옵니다.
- 검색된 컨트롤러에서 메소드를 호출하여 컨트롤러에 도메인 오브젝트에 대한 참조를 제공하십시오.
이 블로그 (다른 작가의)는 대안이지만 유사한 예제를 제공 합니다.
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)에서 제어기를 설정하기 fx:controller위해 fxml 파일에서 속성을 정의 할 수도 없습니다 .
fx:controllerFXML 의 정의에 대한 제한으로 인해 컨트롤러를 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
때로는 결과를 발신자에게 다시 전달할 수도 있습니다.이 경우 관련 질문에 대한 답변을 확인할 수 있습니다.
답변
나는 이것이 매우 오래된 게시물이라는 것을 알고 있으며 이미 훌륭한 답변을 얻었지만 그러한 접근법 중 하나를 보여주기 위해 간단한 MCVE를 만들고 새로운 코더가 개념을 실제로 볼 수있는 방법을 원했습니다.
이 예에서는 5 개의 파일을 사용합니다.
- Main.java- 응용 프로그램을 시작하고 첫 번째 컨트롤러를 호출하는 데 사용됩니다.
- Controller1.java- 첫 번째 FXML 레이아웃의 컨트롤러입니다.
- Controller2.java- 두 번째 FXML 레이아웃의 컨트롤러입니다.
- Layout1.fxml- 첫 번째 장면의 FXML 레이아웃입니다.
- Layout2.fxml- 두 번째 장면의 FXML 레이아웃입니다.
모든 파일은이 게시물의 맨 아래에 전체적으로 나열됩니다.
목표는 : 에서 값을 전달 보여주기 Controller1에 Controller2반대의 경우도 마찬가지.
프로그램 흐름 :
- 첫 번째 장면에는
TextField, aButton및 a가 포함Label됩니다. 를Button클릭하면에 입력 한 텍스트를 포함하여 두 번째 창이로드되어 표시됩니다TextField. - 두 번째 장면에는
TextField, aButton및 a도Label있습니다. 는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>
답변
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 () 메소드도 비슷하게 주석이 달립니다. “
따라서 제어기를 노드와 연관시키고 사용자 데이터를 노드로 설정해야합니다.
답변
다음은 네임 스페이스를 통해 매개 변수를 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이된다는 것을 기억하십시오. 창을로드 한 후에는 다른 구성 요소에 대해 코딩하려는 모든 항목에 동일하게 사용할 수 있습니다.
첫 컨트롤러
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());
}
답변
하나의 컨텍스트 클래스를 작성해야합니다.
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();
이제 전체 애플리케이션에서 모든 컨트롤러에 매개 변수를 전달할 수 있습니다.
답변
그래 넌 할수있어.
첫 번째 컨트롤러를 추가해야합니다.
YourController controller = loader.getController();
controller.setclient(client);
그런 다음 두 번째 클라이언트에서 클라이언트를 선언 한 다음 컨트롤러 하단에서 선언하십시오.
public void setclien(Client c) {
this.client = c;
}
