如何从JavaFX中的另一个控制器类访问UI元素?

时间:2014-02-24 17:58:56

标签: java javafx java-8 fxml

我有一个用NetBeans 8编写的JavaFX / Java 8应用程序(没有SceneBuilder)。

我的应用程序有一个主窗口,它有自己的FXML文件(primary.fxml)和自己的控制器类(FXMLPrimaryController.java)。 FXML中的一个项目是TextArea。 FXMLPrimaryController.java中的一些方法是附加到TextArea

这个应用程序现在生成第二个窗口(另一个" stage"),它有自己的FXML(second.fxml)和它自己的控制器类(FXMLsecondController.java)。

在第二个控制器类中,如何访问主要文件中的TextArea?

以下是相关代码的示例:

primary.fxml:

<Button text="press me!" onAction="#openSecondWindow" />
<TextArea fx:id="myArea" />

FXMLPrimaryController.java:

public class FXMLPrimaryController implements Initializable {

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

    @FXML private TextArea myArea;

    final public void writeToTextArea() {
        myArea.appendText("hi!");
    }

    @FXML
    private void openSecondWindow(ActionEvent event) throws Exception {

        Group root = new Group();
        Stage stage = new Stage();

        AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
        root.getChildren().add(frame);
        Scene scene = new Scene(root);

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

}

second.fxml没什么特别的。假设有一个onAction="#writeSomething"的按钮。

在FXMLsecondController.java中,我想要一个引用上述TextArea的函数。

2 个答案:

答案 0 :(得分:10)

为辅助控制器加载FXML时,请执行以下操作:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();

然后您可以将引用传递给第二个控制器。这可能只是TextArea。

编辑:

替换了上面代码段中的load()来电并添加了setLocation()来电。旧行AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));错误,因为它调用了静态load函数,这在此处没用。

编辑:

(更改了上面的代码段,以便更好地匹配您的变量名称。) 上面的代码片段替换了代码的这一部分:

AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));

该行使用FXMLLoader加载视图,并创建控制器的实例 - 在本例中为FXMLSecondaryController。但是,您不能使用静态FXMLLoader.load方法,需要FXMLLoader的实例。该实例在加载后保存对控制器的引用,您可以使用getController()检索该引用。并且您需要将返回的值强制转换为控制器类(使用(FXMLSecondaryController))。

您的主控制器有一个字段:

@FXML private TextArea myArea;

它包含对TextArea的引用,并由fxml加载器初始化。 (如果删除@FXML注释,则加载程序将不会触及它。)

在辅助控制器中,添加以下字段:

public TextArea primaryTextArea;

请注意@FXML已消失(fxml加载程序不会触及该字段)以及private(您想设置字段,因此其他人必须看到它)。 现在,您可以在加载控制器后设置此字段。所以回到加载代码:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();
// Add this too:
c.primaryTextArea = myArea;

编辑:替换了上面代码段中的load()来电并添加了setLocation()来电。旧行AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));错误,因为它调用了静态load函数,这在此处没用。

FXMLSecondaryController现在引用了主控制器的TextArea。在其中一种方法中,您应该能够访问其内容:

public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

    @FXML
    private void doSomething(ActionEvent event) throws Exception {
        primaryTextArea.appendText("Hi ho!");
    }

}

请注意,此解决方案不是最佳方法,但它很简单,可以作为一个开始。当然建议使用绑定。我会尝试创建一个包含数据的类,其他控制器绑定在该类上。但如果你现在采取简单的方法,那就足够了。

答案 1 :(得分:3)

我希望你能从FXMLPrimaryController公开TextField,并在FXMLsecondController中有一个TextField属性;然后在将各个部分组合在一起时设置该属性。但这不是一个好的设计,因为你将通过控制器公开视图的细节。

我确信你不想要TextArea而是second.fxml中TextArea的数据。所以我建议在每个控制器中公开一个String属性并将它们绑定在一起。

public class FXMLPrimaryController {  
     private ReadOnlyStringWrapper text ;  

     @FXML 
     private TextArea myArea;

     public void initialize() {  
          text= new ReadOnlyStringWrapper(this, "text");  
          text.bind(myArea.textProperty());  
     }  
     public ReadOnlyStringProperty textProperty() {  
          return text.getReadOnlyProperty();  
     }  
     public String getText() {  
          return text.get();  
     }  

    @FXML
    private void openSecondWindow(ActionEvent event) throws Exception {

    Group root = new Group();
    Stage stage = new Stage();

    AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
    root.getChildren().add(frame);
    Scene scene = new Scene(root);

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

然后在你的FXMLsecondController中,你可以拥有

public class FXMLsecondController {  
     private StringProperty text ;  
     @FXML  
     private void handleButtonAction(ActionEvent event) {  
          System.out.println(text.getValue());  
     }  
     public void initialize() {  
          text = new SimpleStringProperty(this, "text");  
     }  
     public StringProperty textProperty() {  
          return text ;  
     }  
     public String getText() {  
          return text.get();  
     }  
}

然后你可以使用类似的东西绑定这两个

FXMLLoader primaryLoader = new FXMLLoader(getClass().getResource("primary.fxml"));  
Parent textAreaHolder = (Parent) primaryLoader.load();  
FXMLLoader secondaryLoader = new FXMLLoader(getClass().getResource("second.fxml"));  
Parent textAreaUser = (Parent) secondaryLoader.load();  
FXMLPrimaryController primaryController = (FXMLPrimaryController) textAreaHolder.getController();  
FXMLsecondController secondController = (FXMLsecondController) textAreaUser.getController();  
secondController.textProperty().bind(primaryController.textProperty());

现在绑定两个控制器的变量text,您可以轻松使用在辅助控制器中主控制器的textarea中输入的值!