传递参数javafx fxml

3z6pesqy  于 2021-07-06  发布在  Java
关注(0)|答案(10)|浏览(476)

如何在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作为参数传递)。
有什么想法吗?
im9ewurl

im9ewurl1#

下面是一个使用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是舞台中场景的根。这与示例(我的特定用例的实现细节)无关,但决定保留它,因为有些人可能会发现它很有用。

6yoyoihd

6yoyoihd2#

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

amrnrhlw

amrnrhlw3#

下面是一个通过名称空间向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();
    }
}
wtzytmuj

wtzytmuj4#

是的,你可以。
您需要添加第一个控制器:

YourController controller = loader.getController();     
controller.setclient(client);

然后在第二个中声明一个客户机,然后在控制器的底部:

public void setclien(Client c) {
    this.client = c;
}
ef1yzkbh

ef1yzkbh5#

这很管用。。
请记住,第一次打印传递的值时,您将得到null,您可以在加载windows之后使用它,这与您要为任何其他组件编码的所有内容相同。
第一控制器

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

klsxnrf16#

必须创建一个上下文类。

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

现在您可以将参数从整个应用程序传递到任何控制器。

yhived7q

yhived7q7#

为什么要回答一个6岁的问题?
任何编程语言最基本的概念之一就是如何从一个(窗口、窗体或页面)导航到另一个。此外,在执行此导航时,开发人员通常希望从一个(窗口、窗体或页面)传递数据,并显示或使用传递的数据
虽然这里的大多数答案都提供了很好到很好的例子来说明如何做到这一点,但我们认为我们会把它提高一个或两个或三个档次
我们说三是因为我们将在三个(窗口、窗体或页面)之间导航,并使用静态变量的概念在(窗口、窗体或页面)周围传递数据
我们还将在导航时包含一些决策代码

public class Start extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        // This is MAIN Class which runs first
        Parent root = FXMLLoader.load(getClass().getResource("start.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setResizable(false);// This sets the value for all stages
        stage.setTitle("Start Page"); 
        stage.show();
        stage.sizeToScene();
    }

    public static void main(String[] args) {
        launch(args);
    } 
}

启动控制器

public class startController implements Initializable {

@FXML Pane startPane,pageonePane;
@FXML Button btnPageOne;
@FXML TextField txtStartValue;
public Stage stage;
public static int intSETonStartController;
String strSETonStartController;

@FXML
private void toPageOne() throws IOException{

    strSETonStartController = txtStartValue.getText().trim();

        // yourString != null && yourString.trim().length() > 0
        // int L = testText.length();
        // if(L == 0){
        // System.out.println("LENGTH IS "+L);
        // return;
        // }
        /* if (testText.matches("[1-2]") && !testText.matches("^\\s*$")) 
           Second Match is regex for White Space NOT TESTED !
        */

        String testText = txtStartValue.getText().trim();
        // NOTICE IF YOU REMOVE THE * CHARACTER FROM "[1-2]*"
        // NO NEED TO CHECK LENGTH it also permited 12 or 11 as valid entry 
        // =================================================================
        if (testText.matches("[1-2]")) {
            intSETonStartController = Integer.parseInt(strSETonStartController);
        }else{
            txtStartValue.setText("Enter 1 OR 2");
            return;
        }

        System.out.println("You Entered = "+intSETonStartController);
        stage = (Stage)startPane.getScene().getWindow();// pane you are ON
        pageonePane = FXMLLoader.load(getClass().getResource("pageone.fxml"));// pane you are GOING TO
        Scene scene = new Scene(pageonePane);// pane you are GOING TO
        stage.setScene(scene);
        stage.setTitle("Page One"); 
        stage.show();
        stage.sizeToScene();
        stage.centerOnScreen();  
}

private void doGET(){
    // Why this testing ?
    // strSENTbackFROMPageoneController is null because it is set on Pageone
    // =====================================================================
    txtStartValue.setText(strSENTbackFROMPageoneController);
    if(intSETonStartController == 1){
      txtStartValue.setText(str);  
    }
    System.out.println("== doGET WAS RUN ==");
    if(txtStartValue.getText() == null){
       txtStartValue.setText("");   
    }
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // This Method runs every time startController is LOADED
     doGET();
}    
}

第页,共页

wpx232ag

wpx232ag8#

推荐的方法
这个答案列举了将参数传递给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(loader.load())
  );

  CustomerDialogController controller = loader.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
从fxmloader示例获取控制器。
在检索到的控制器上调用方法以向控制器提供对域对象的引用。
这个博客(另一位作者写的)提供了一个类似的例子。
在fxmlloader上设置控制器

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = loader.load();

您可以在代码中构造一个新的控制器,将您想要的任何参数从调用者传递到控制器构造函数中。一旦构造了控制器,就可以在调用 load() 示例方法。
要在加载器上设置控制器(在javafx2.x中),也不能定义 fx:controller 属性。
由于对 fx:controller 在fxml中,我个人更喜欢从fxmlloader获取控制器,而不是将控制器设置到fxmlloader中。
让控制器从外部静态方法检索参数
sergey对controller.java文件中javafx2.0how-to application.getparameters()的回答就是这个方法的例子。
使用依赖注入
fxmloader支持guice、spring或javaeecdi等依赖注入系统,允许您在fxmloader上设置自定义控制器工厂。这提供了一个回调,您可以使用该回调创建控制器示例,该示例具有由相应的依赖注入系统注入的依赖值。
以下答案提供了使用spring的javafx应用程序和控制器依赖注入的示例:
在javafx中添加spring依赖注入(jpa repo,service)
afterburner.fx框架是一个非常好的、干净的依赖注入方法,它使用了一个示例air hacks应用程序。afterburner.fx依赖于jee6 javax.inject来执行依赖注入。
使用事件总线
最初的fxml规范创建者和实现者gregbrown经常建议考虑使用事件总线(如guava eventbus)在fxml示例化的控制器和其他应用程序逻辑之间进行通信。
eventbus是一个简单但功能强大的发布/订阅api,它带有注解,允许pojo在jvm中的任何地方相互通信,而不必互相引用。
后续问答
第一种方法,你为什么要回到舞台上?方法也可以是void,因为您已经给出了show()命令;就在返回阶段之前;。你如何通过返回舞台来计划使用
它是一个问题的功能性解决方案。舞台从舞台返回 showCustomerDialog 函数,以便外部类可以存储对它的引用,该类可能希望在以后执行某些操作,例如基于主窗口中的按钮单击隐藏阶段。另一种面向对象的解决方案可以将功能和stage引用封装在customerdialog对象中,或者具有customerdialog扩展stage。一个面向对象接口到封装fxml、控制器和模型数据的自定义对话框的完整示例超出了这个答案的范围,但是对于任何想创建一个接口的人来说,这可能是一篇很有价值的博客文章。
stackoverflow用户@dzim提供的附加信息
spring引导依赖注入示例
关于如何使用“spring引导方式”的问题,有一个关于javafx2的讨论,我在附带的permalink中对此进行了讨论。该方法仍然有效,并于2016年3月在spring boot v1.3.3版本上进行了测试:https://stackoverflow.com/a/36310391/1281217
有时,您可能希望将结果返回给调用者,在这种情况下,您可以查看相关问题的答案:
javafx-fxml参数从控制器a传递到b并返回

fafcakar

fafcakar9#

我意识到这是一个非常老的帖子,已经有了一些很好的答案,但是我想做一个简单的mcve来演示这样一种方法,让新的程序员能够快速地看到这个概念的实际应用。
在本例中,我们将使用5个文件:
java—用于启动应用程序并调用第一个控制器。
controller1.java—第一个fxml布局的控制器。
controller2.java—第二个fxml布局的控制器。
layout1.fxml-第一个场景的fxml布局。
layout2.fxml-第二个场景的fxml布局。
所有文件都列在这篇文章的底部。
目标:证明价值观从 Controller1Controller2 反之亦然。
程序流程:
第一个场景包含 TextField ,一个 Button ,和 Label . 当 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();

    }
}

控制器1.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);
    }
}

控制器2.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());
    }

}

布局1.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>

布局2.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>
omqzjyyz

omqzjyyz10#

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()方法的注解类似。”
因此,需要将控制器与节点相关联,并为节点设置用户数据。

相关问题