传递参数JavaFX FXML

如何将parameter passing给javafx中的辅助窗口? 有没有办法与相应的控制器进行通信?

例如:用户从TableViewselect一个客户,打开一个新窗口,显示客户的信息。

 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控制器的不同机制。

对于小型应用程序,我强烈build议直接从调用者传递参数给控制器 – 这很简单,直接,不需要额外的框架。

对于更大,更复杂的应用程序,如果要在应用程序中使用dependency injection或事件总线机制,则值得研究。

直接从主叫方传递参数给控制器

将自定义数据传递给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. 在指定位置构build一个基于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(); 

你可以在代码中构造一个新的控制器,将你想调用的任何parameter passing给控制器​​的构造函数。 一旦你构build了一个控制器,你可以调用load() 实例方法之前将其设置在FXMLLoader实例上。

要在加载器上设置一个控制器(在JavaFX 2.x中),你也不能在你的fxml文件中定义一个fx:controller属性。

由于FXML中fx:controller定义的限制,我个人更喜欢从FXMLLoader获取控制器,而不是将控制器设置为FXMLLoader。

使控制器从外部静态方法中检索参数

此方法由Sergey对“ Controller.java”文件中的Javafx 2.0 How-to Application.getParameters()的回答进行了说明 。

使用dependency injection

FXMLLoader允许您在FXMLLoader上设置自定义控制器工厂,从而支持Guice,Spring或Java EE CDI等dependency injection系统。 这提供了一个callback,您可以使用该callback来创build具有由相应的dependency injection系统注入的依赖值的控制器实例。 有一个将FXML与Springdependency injection系统集成的例子(不幸的是链接已经死了,内容已经不存在,如果有人知道类似的例子,请编辑这个问题来引用它),尽pipe它比它更笨使用JavaFX 2.2中提供的新的自定义控制器工厂特性。

afterburner.fx框架提供了一个非常好的干净的dependency injection方法,其中包含一个使用它的示例air-hacks应用程序 。 afterburner.fx依赖于JEE6的javax.inject来执行dependency injection。

使用事件总线

最初的FXML规范创build者和实现者Greg Brown经常build议考虑使用事件总线来实现FXML实例化控制器与其他应用程序逻辑之间的通信。

EventBus是一个简单但function强大的发布/订阅API,带有注释,允许POJO在JVM中的任何位置彼此通信,而无需互相参考。

后续问答

在第一种方法,为什么你返回舞台? 该方法也可以是无效的,因为你已经给了show(); 就在返回阶段之前。 您如何通过返回舞台来计划使用情况?

这是解决问题的function。 从showCustomerDialog函数返回一个阶段,以便对其的引用可以被外部类存储,这个外部类可能希望做一些事情,比如在稍后的时间基于主窗口中的button点击来隐藏舞台。 另一种面向对象的解决scheme可以将function和阶段引用封装在CustomerDialog对象中,或者具有CustomerDialog扩展阶段。 一个面向对象的接口封装FXML,控制器和模型数据的自定义对话框的完整示例超出了这个答案的范围,但是对于任何倾向于创build一个的博客post来说,这可能是一个有价值的博客文章。


由名为@dzim的StackOverflow用户提供的附加信息

Spring Bootdependency injection的例子

关于如何做到“Spring Boot Way”的问题,有关于JavaFX 2的讨论,我在附加的永久链接中进行了解答。 该方法仍然有效,并在2016年3月的Spring Boot v1.3.3上进行testing.RELEASE: https ://stackoverflow.com/a/36310391/1281217

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允许控制器文档中定义的标记调用它,在第二个例子中,button字段被注释为允许加载器设置它的值,initialize()方法也被类似地注释掉。

因此,您需要将控制器与节点关联,并将用户数据设置为该节点。

这是一个通过命名空间将parameter passing给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> 

为名称空间variableslabelText定义值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(); } } 

这工作..

记住第一次打印传递值时,你会得到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()); } 

你必须创build一个上下文类。

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

现在您可以将parameter passing给整个应用程序的任何控制器。

这里是一个使用由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的中心。 这与示例(我的具体用例的实现细节)无关,但决定放弃它,因为有些人可能会觉得它有用。