使用 JavaFX 将数据传递到已打开的 window

Using JavaFX to pass data to an already opened window

我是 JavaFX 的新手。这在没有 FXML 的情况下很容易做到,但是 FXML 控制器难倒了我。

我正在尝试做的事情:设置一个带有按钮的主 window。单击时,该按钮会启动第二个弹出窗口 window,用户可以在其中提交一个值。在关闭第二个 window 时(当前通过单击弹出窗口的按钮完成),我希望将用户的输入传递回主控制器——主 window 已经打开。

到目前为止,我有 2 个 .fxml 文件(一个用于主 window,另一个用于弹出窗口),以及相应的控制器:MainWindowController:

public class MainController implements Initializable {

@FXML
public Label label;
@FXML
private Button button;


@FXML
private void popBtnClick(ActionEvent event) throws IOException {
    //creates new pop-up window
    Stage popupSave = new Stage();
    popupSave.initModality(Modality.APPLICATION_MODAL);
    popupSave.initOwner(ComWins.stage);

    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("PopUp.fxml"));
    Parent root = loader.load();

    PopUpController controller = loader.getController();

    //calls a method in the PopUpController, and uses it to pass data to 
    //the Popup window.
    controller.dataToPopUp(7);

    Scene scene = new Scene(root);
    popupSave.setScene(scene);
    popupSave.showAndWait();
}

I also tried calling this method from the popup window with no success in 
changing Main's label.
public void dataPass(String name){
    label.setText(name);
}


@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

和 PopUpController:

public class PopUpController implements Initializable {

@FXML
private Button ok_btn; 
@FXML
public TextField input_tf;
@FXML
private String input;




@FXML
private void okBtnClick() throws IOException {
    input = input_tf.getText();

    /*my attempt to pass the variable-- using a loader to get the 
     controller and then referencing the public label. */ 
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("Main.fxml"));
    Parent root = loader.load();


    FXMLDocumentController controller = loader.getController();

    //this line works, and retrieves the label's text.
    String temp = controller.label.getText();

    //but this line does not work. Why? 
    controller.label.setText(input);


    //closes this pop-up
    Stage stage = (Stage)input_tf.getScene().getWindow();
    stage.close();


}

//this method is called in the maincontroller and used to pass data to 
//the popup's textfield.
public void dataToPopUp(int x){
    input_tf.setText(Integer.toString(x));
}


 @Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

使用上面的方法,Main 将 ('7') 传递到 PopUp 的文本字段中。但是,如果用户在文本字段中输入其他内容,我似乎无法将该数据返回到 Main。这就像有一个设置弹出窗口 window,然后将用户的选择从设置弹出窗口传递回主窗口 window。我只是想不出如何将事情传回主要 window。

我没有使用 SpringBoot,但感谢您的建议。

提前致谢!

我就是这样做的。 首先创建一个接口

public interface DataSender {
 void send(String data);
}

然后将该接口实现到您的主 Window 控制器

public class FXMLDocumentController implements Initializable,DataSender {

@FXML
private Label label;

@FXML
private void handleButtonAction(ActionEvent event) {

    try {
        Stage popupSave = new Stage();
        popupSave.initModality(Modality.NONE);
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("PopUp.fxml"));
        Parent root = loader.load();

        PopUpController controller = loader.getController();
        controller.setX(7);
        //this is the line use to get data from popup
        controller.setSendDataSender(this);

        Scene scene = new Scene(root);
        popupSave.setScene(scene);
        popupSave.show();
    } catch (IOException ex) {
        Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
    }
}


//this method can call from popus controller
@Override
public void send(String data) {
    label.setText(data);
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}
}

然后重写实现的接口方法,在方法内部你可以更改 Main window UI data I changed label text.

然后在 pupup 控制器中创建类型为 DataSender 的变量(之前创建的接口)并创建方法来设置该接口

public class PopUpController implements Initializable {

private int x;

//this is the variable
private DataSender dataSender;


@FXML
private Button btnOk;
@FXML
private TextField txtText;

@Override
public void initialize(URL url, ResourceBundle rb) {
    btnOk.setOnAction(e->{
        //When Click this button will call Main Window send method
        dataSender.send(txtText.getText());
    });
}

public void setX(int x){
    this.x=x;
}

//this is the method to set variable value from Main Window
public void setSendDataSender(DataSender ds){
    this.dataSender=ds;
}

}

然后当弹出 window 按钮被点击时调用发送数据的方法,然后将改变主 window 标签文本。 此解决方案适用于 me.hope 这对您也很有用。

如果您使用的是 Spring Boot,YouTube 上的 MPV Java 有很好的示例,可以将您的控制器连接在一起,让您可以在它们之间干净、轻松地传递信息。
在我的application 我已经能够实现这些示例。每个控制器都使用 @Component 注册为一个 bean,这意味着您可以 @Autowire 将控制器添加到另一个控制器中。在您的主控制器中,我建议设置一些基本的 getters/setters 以允许与您的字段进行外部交互,以便其他控制器可以 "talk" 到主控制器。

一个真正基本的例子是:

@Component
public class MyMainController {
    @FXML private TextField exampleTextField;
    ...
    ...
    /* Get the text of a field from this controller: can be accessed from another controller */
    public String getExampleTextField() {
        exampleTextField.getText();
    }
    /* Set the text of a field on this controller: can be accessed from another controller */
    public void setExampleTextField(String text) {
        exampleTextField.setText(text);
    }
}
@Component
public class AnotherController {
    @Autowired private MyMainController myMainController;
    ...
    ...
    public void someMethod(String newText) {
        // Do some work here and set some text back to the main controller
        myMainController.setExampleTextField(newText);
    }
}

MPV Java 在解释这个概念方面做得更好。
https://www.youtube.com/watch?v=hjeSOxi3uPg

我查看了这些建议,但无法为我找到任何有用的东西——许多概念都让我难以理解,因为我是 Java 的新手。经过几次尝试,我能够得到一个相当简单的问题解决方案。它可能远非最佳实践,但它有效:

在主window的控制器中,使用弹出窗口的控制器调用弹出窗口的字符串变量并将其设置为标签的文本(label.setText(controller.test))。字符串变量必须为 public,并在单击按钮关闭弹出窗口后设置。请看下面的代码:

Main.fxml:

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="switchingpass.MainController">
    <children>
        <Button fx:id="button" layoutX="126" layoutY="90" onAction="#popBtnClick" text="Click Me!" />
        <Label fx:id="label2" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
      <Label fx:id="label" layoutX="143.0" layoutY="38.0" text="Label" />
    </children>
</AnchorPane>

主控制器:

public class MainController implements Initializable {

@FXML
public Label label;
@FXML
private Button button;

@FXML
private void popBtnClick(ActionEvent event) throws IOException {
    //creates new pop-up window
    Stage popupSave = new Stage();
    popupSave.initModality(Modality.APPLICATION_MODAL);
    popupSave.initOwner(SwitchingPass.stage);

    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("PopUp.fxml"));
    Parent root = loader.load();

    PopUpController controller = loader.getController();

    Scene scene = new Scene(root);
    popupSave.setScene(scene);
    popupSave.showAndWait();

    //after the popup closes, this will run, setting the label's text to the popup's test variable, which is public.
    label.setText(controller.test);
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

PopUp.fxml:

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="switchingpass.PopUpController">
   <children>
      <TextField fx:id="input_tf" layoutX="207.0" layoutY="65.0" />
      <Button fx:id="close_btn" layoutX="268.0" layoutY="185.0" mnemonicParsing="false" onAction="#closeBtnClick" text="Close" />
   </children>
</AnchorPane>

弹出控制器:

public class PopUpController implements Initializable {

@FXML
public Button close_btn; 
@FXML
public TextField input_tf;
@FXML
public String test;


@FXML
private void closeBtnClick() throws IOException {

    //stores textfield input as a string    
    test = input_tf.getText();

    Stage stage = (Stage)input_tf.getScene().getWindow();
    stage.close();
}

 @Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
}    

}

请注意,扩展应用程序的代码的主要 class 必须将阶段变量声明为 public 静态变量:

public class SwitchingPass extends Application {

    //this is necessary to initialize the owner of the popup
    public static Stage stage;

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

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

}

同样,这可能不是完成此任务的最佳方法,但它确实有效,而且也许对其他人有帮助。