【问题标题】:Using JavaFX to pass data to an already opened window使用 JavaFX 将数据传递到已打开的窗口
【发布时间】:2020-01-29 00:36:51
【问题描述】:

我是 JavaFX 的新手。这很容易在没有 FXML 的情况下完成,但是 FXML 控制器让我很难过。

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

到目前为止,我有 2 个 .fxml 文件(一个用于主窗口,另一个用于弹出窗口),以及相应的控制器: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。这就像有一个设置弹出窗口,然后将用户从设置弹出窗口中的选择传递回主窗口。我只是不知道如何将内容传递回主窗口。

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

提前谢谢你!

【问题讨论】:

标签: javafx controller fxml fxmlloader


【解决方案1】:

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

一个非常基本的例子是:

@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

【讨论】:

  • 我检查了链接,但我没有使用 Spring Boot。谢谢。
【解决方案2】:

我查看了这些建议,但无法找到任何适合我的方法——因为我是 Java 新手,所以很多概念都超出了我的想象。经过几次尝试,我能够得到一个相当简单的解决方案。这可能远非最佳实践,但它确实有效:

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

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
}    

}

请注意,扩展 Application 的代码主类必须将阶段变量声明为公共静态变量:

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

}

再说一次,这可能不是实现此目的的最佳方式,但它确实有效,并且可能对其他人有所帮助。

【讨论】:

  • 一旦舞台关闭,控制器的变量就会设置为空。至少,在我的版本中是这样的(Java 11.0 和 FXML 15.0.1)
【解决方案3】:

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

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

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

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
}
}

然后重写实现的接口方法,在方法里面你可以改变主窗口UI数据我改变了标签文本。

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

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;
}

}

然后当单击弹出窗口按钮时,使用数据调用发送方法,然后将更改主窗口标签文本。 这个解决方案对我有用。希望这对你也有用。

【讨论】:

  • 我通过在自己的类中创建接口来尝试这个(我是使用接口的新手),所以请告诉我这是否正确。我相应地进行了其他更改,但出现错误: > (this) is getting an incompatible types error: FXMLDocumentController cannot be convert to DataSender 我没有使用“this”的经验所以我自己无法解决它,现在正在阅读它。知道我做错了什么吗?
  • 在单独的文件中创建接口。不要在自己的类中创建它,并确保您实现了该类的接口,否则它不起作用。 [公共类FXMLDocumentController实现Initializable,DataSender]
  • Java中的关键字'THIS'是一个引用变量,它引用当前对象。在此代码中 setSendDataSender(DataSender ds);方法请求 DataSender 对象,当我们实现 FXMLDocumentController 的 DataSender 接口时,它变成了一个 DataSender,所以我们可以使用这个关键字作为 DataSender,因为 FXMLDocumentController 是一个 DataSender。
猜你喜欢
  • 2021-03-16
  • 2014-10-19
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多