首页 文章

传递参数JavaFX FXML

提问于
浏览
157

如何将参数传递给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 将成为新窗口 . 问题是,我可以__31605_的信息(通过传递id作为参数) .

有任何想法吗?

8 回答

  • 226

    以下是使用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的中心,该BoarderPane是舞台中场景的根 . 这与示例(我的具体用例的实现细节)无关,但决定将其保留,因为有些人可能觉得它很有用 .

  • 7

    This WORKS ..

    Remember first time you print the passing value you will get null, You can use it after your windows loaded , same for everything you want to code for any other component.

    第一控制器

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

    Recommended Approach

    这个答案列举了将参数传递给FXML控制器的不同机制 .

    对于小型应用程序,我强烈建议将参数直接从调用者传递给控制器 - 它简单,直接,不需要额外的框架 .

    对于更大,更复杂的应用程序,如果要在应用程序中使用Dependency InjectionEvent Bus机制,则值得研究 .

    Passing Parameters Directly From the Caller to the Controller

    通过从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实例获取控制器 .

    • 在检索到的控制器上调用方法,以向控制器提供对域对象的引用 .

    这个博客(由另一位作家提供)提供了一个替代的,但类似的,example .

    Setting a Controller on the 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 .

    Having the Controller Retrieve Parameters from an External Static Method

    这种方法的例子是谢尔盖对Javafx 2.0 How-to Application.getParameters() in a Controller.java file的回答 .

    Use Dependency Injection

    FXMLLoader支持依赖注入系统,如Guice,Spring或Java EE CDI,允许您在FXMLLoader上设置自定义控制器工厂 . 这提供了一个回调,您可以使用该回调来创建具有相应依赖注入系统注入的依赖值的控制器实例 . 有一个集成FXML with the Spring dependency injection system的样本(不幸的是链接已经死了,内容已经消失,如果有人知道类似的例子,请编辑这个问题以引用它),虽然它比使用新的自定义控制器有点笨拙工厂功能在JavaFX 2.2中提供 .

    一个非常好的,干净的依赖注入方法由afterburner.fx framework以及使用它的样本air-hacks application举例说明 . afterburner.fx依赖于JEE6 javax.inject来执行依赖注入 .

    Use an Event Bus

    最初的FXML规范创建者和实现者Greg Brown经常建议考虑使用event bus来实现FXML实例化控制器和其他应用程序逻辑之间的通信 .

    EventBus是一个简单但功能强大的发布/订阅API,带有注释,允许POJO在JVM中的任何位置相互通信,而无需相互引用 .

    Follow-up Q&A

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

    它是解决问题的功能性解决方案 . 从 showCustomerDialog 函数返回一个阶段,以便对可能希望执行某些操作的外部类存储对它的引用,例如在稍后的时间基于主窗口中的按钮单击隐藏阶段 . 另一种面向对象的解决方案可以将功能和阶段引用封装在CustomerDialog对象中,或者具有CustomerDialog扩展阶段 . 自定义对话框封装的面向对象接口的完整示例FXML,控制器和模型数据超出了这个答案的范围,但可能会为任何倾向于创建它的人制作一篇有 Value 的博客文章 .


    Additional information supplied by StackOverflow user named @dzim

    Spring Boot Dependency Injection的示例

    关于如何做到的问题"The Spring Boot Way",有一个关于JavaFX 2的讨论,我在附加的永久链接中对此进行了回答 . 该方法仍然有效,并于2016年3月在Spring Boot v1.3.3上进行了测试.RELEASE:https://stackoverflow.com/a/36310391/1281217


    有时,您可能希望将结果传递回调用者,在这种情况下,您可以查看相关问题的答案:

  • 0

    我意识到这是一篇非常古老的帖子,并且已经有了一些很好的答案,但我想制作一个简单的MCVE来演示一种这样的方法,并允许新的编码人员快速看到实际的概念 .

    在这个例子中,我们将使用5个文件:

    • Main.java - 仅用于启动应用程序并调用第一个控制器 .

    • Controller1.java - 第一个FXML布局的控制器 .

    • Controller2.java - 第二个FXML布局的控制器 .

    • Layout1.fxml - 第一个场景的FXML布局 .

    • Layout2.fxml - 第二个场景的FXML布局 .

    所有文件都在本文的底部完整列出 .

    The Goal: 演示从 Controller1Controller2 的传递值,反之亦然 .

    The Program Flow:

    • 第一个场景包含 TextFieldButtonLabel . 单击 Button 时,将加载并显示第二个窗口,包括在 TextField 中输入的文本 .

    • 在第二个场景中,还有一个 TextField ,一个 Button 和一个 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>
    
  • 3

    以下是通过命名空间将参数传递给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();
        }
    }
    
  • 2

    您必须创建一个Context类 .

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

    现在您可以将参数传递给整个应用程序的任何控制器 .

  • 6

    您可以决定使用公共可观察列表来存储公共数据,或者只创建一个公共setter方法来存储数据并从相应的控制器中检索

  • 0

    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在短语"In the first version, the handleButtonAction() is tagged with @FXML to allow markup defined in the controller's document to invoke it. In the second example, the button field is annotated to allow the loader to set its value. The initialize() method is similarly annotated."之前

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

相关问题