Alv*_*aro 180 parameters dependency-injection javafx parameter-passing fxml
如何将参数传递给javafx中的辅助窗口?有没有办法与相应的控制器通信?
例如:用户从a中选择一个客户,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();
}
}
Run Code Online (Sandbox Code Playgroud)
newStage将是新窗口.问题是,我找不到告诉控制器在哪里查找客户信息的方法(通过传递id作为参数).
有任何想法吗?
jew*_*sea 256
推荐方法
这个答案列举了将参数传递给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());
}
}
Run Code Online (Sandbox Code Playgroud)
构造一个新的FXMLLoader,如示例代码所示new FXMLLoader(location).该位置是一个URL,您可以通过以下方式从FXML资源生成此类URL:
new FXMLLoader(getClass().getResource("sample.fxml"));
Run Code Online (Sandbox Code Playgroud)
注意不要在FXMLLoader上使用静态加载功能,否则您将无法从加载器实例中获取控制器.
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();
Run Code Online (Sandbox Code Playgroud)
您可以在代码中构造一个新的控制器,将您想要的任何参数从调用者传递到控制器构造函数中.构建控制器后,可以在调用load() 实例方法之前在FXMLLoader实例上进行设置.
要在加载器上设置控制器(在JavaFX 2.x中),您也不能fx:controller在fxml文件中定义属性.
由于fx:controllerFXML中定义的限制,我个人更喜欢从FXMLLoader获取控制器,而不是将控制器设置为FXMLLoader.
让控制器从外部静态方法中检索参数
这个方法的例子是Sergey 在Controller.java文件中对Javafx 2.0 How-to Application.getParameters()的回答.
使用依赖注入
FXMLLoader支持依赖注入系统,如Guice,Spring或Java EE CDI,允许您在FXMLLoader上设置自定义控制器工厂.这提供了一个回调,您可以使用该回调来创建具有相应依赖注入系统注入的依赖值的控制器实例.有一个将FXML与Spring依赖注入系统集成的示例(遗憾的是链接已经死了,内容已经消失,如果有人知道类似的例子,请编辑这个问题以引用它),虽然它有点笨拙但不会使用JavaFX 2.2中提供的新自定义控制器工厂功能.
一个非常好的,干净的依赖注入方法的例子是afterburner.fx框架和一个使用它的示例air-hacks应用程序.afterburner.fx依赖于JEE6 javax.inject来执行依赖注入.
使用事件总线
最初的FXML规范创建者和实现者Greg Brown经常建议考虑使用事件总线在FXML实例化控制器和其他应用程序逻辑之间进行通信.
EventBus是一个简单但功能强大的发布/订阅API,带有注释,允许POJO在JVM中的任何位置相互通信,而无需相互引用.
后续问答
在第一种方法上,你为什么要回归舞台?该方法也可以是空的,因为你已经给出了命令show(); 就在返回阶段之前; 你如何通过返回舞台来规划用法
它是解决问题的功能性解决方案.从该showCustomerDialog函数返回一个阶段,以便可以由外部类存储对它的引用,该外部类可能希望做某事,例如在稍后的时间基于主窗口中的按钮单击来隐藏阶段.另一种面向对象的解决方案可以将功能和阶段引用封装在CustomerDialog对象中,或者具有CustomerDialog扩展阶段.封装FXML,控制器和模型数据的自定义对话框的面向对象接口的完整示例超出了本答案的范围,但可能会为任何倾向于创建一个的人发布一篇有价值的博客文章.
StackOverflow用户提供的附加信息,名为@dzim
Spring Boot依赖注入的示例
关于如何做到这一点的问题"春季引导方式",有一个关于JavaFX 2的讨论,我在附加的永久链接中对此进行了回答.该方法在2016年3月Spring Boot v1.3.3上仍然有效并经过测试.请访问:https://stackoverflow.com/a/36310391/1281217
有时,您可能希望将结果传递回调用者,在这种情况下,您可以查看相关问题的答案:
我意识到这是一篇非常古老的帖子,并且已经有了一些很好的答案,但我想制作一个简单的MCVE来演示一种这样的方法,并让新编码员能够快速看到这个概念.
在这个例子中,我们将使用5个文件:
所有文件都在本文的底部完整列出.
目的:为了证明从传递值Controller1到Controller2,反之亦然.
计划流程:
TextField,a Button和a Label.当Button被点击时,第二个窗口中加载并显示,包括输入的文本TextField.TextField,a Button和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();
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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());
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
定义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();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
159157 次 |
| 最近记录: |