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

2022-09-03 07:15:29

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

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

此应用程序现在生成第二个窗口(另一个“阶段”),其中包含自己的 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


答案 1

加载辅助控制器的 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加载视图,并且还创建控制器的实例 - 在本例中。但是,不能为此使用静态方法,需要 一个 实例。该实例在加载后保留对控制器的引用,您可以使用 检索该实例。您需要将返回的值转换为控制器类(带有 )。FXMLSecondaryControllerFXMLLoader.loadFXMLLoadergetController()(FXMLSecondaryController)

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

@FXML private TextArea myArea;

这包含对您的引用,并由 fxml 加载程序初始化。(如果移除批注,加载程序将不会触摸它。TextArea@FXML

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

public TextArea primaryTextArea;

请注意,它消失了(fxml加载器不应触摸字段)以及(您想要设置字段,因此其他人必须看到它)。现在,您可以在加载控制器后设置此字段。所以回到加载代码:@FXMLprivate

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

现在具有对 主控制器 的引用。在其中一种方法中,您应该能够访问其内容:FXMLSecondaryControllerTextArea

public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

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

}

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


答案 2

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

我相信你不想要文本区域,而是文本区域的数据。因此,我的建议是在每个控制器中公开一个 String 属性,并将它们绑定在一起。second.fxml

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


推荐