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

Posted

技术标签:

【中文标题】如何从 JavaFX 中的另一个控制器类访问 UI 元素?【英文标题】:How do I access a UI element from another controller class in JavaFX? 【发布时间】:2014-03-26 13:32:19 【问题描述】:

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

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

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

编辑:

替换了上面sn-p中的load()调用,添加了setLocation()调用。旧行AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml")); 是错误的,因为它调用了静态的load 函数,在这里没有用。

编辑:

(更改了上面的代码 sn-p 以更好地匹配您的变量名称。) 上面的代码 sn-p 替换了这部分代码:

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;

注意@FXMLprivate 都消失了(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;

编辑:替换了上面 sn-p 中的 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!");
    


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

【讨论】:

请看一下我提供的示例代码。我对您的回答不理解的是,我的主窗口是在初始化应用程序时初始化的。但是第二个窗口是由用户请求(他们按下一个按钮)。一旦第二个窗口打开,假设窗口有一个按钮,我希望按钮中的代码访问第一个窗口中的 textArea。 当您使用 FXMLLoader.load 加载辅助控制器时,您可以更改该行并添加我的答案中的代码。您还在辅助控制器中创建一个包含 TextArea 的字段:“TextArea primaryTextArea;”。因此,当您创建辅助控制器时,请将 primaryTextArea 字段设置为 myArea。您不需要函数来查询 TextArea;您可以在创建辅助控制器时传递它。 对不起 - 还是有点困惑。如果我使用上面的代码替换我加载第二个窗口/第二个 FXML 内容的一些代码......那么我不明白这一行是什么: FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();当我在第二个 Controller.java 中结束时,我是如何从主窗口访问 TextArea 的? “c”不可用...?是吗? 如果您给我一点时间,我稍后会添加更好的信息。目前我在我的 iPhone 上,在这里编辑相当不安 :-) 现在:该行使您可以访问要访问 TextArea 的第二个控制器。在该辅助控制器中,您只需使用字段 primaryTextArea 即可访问 TextArea。 是的,慢慢来。我感谢您的帮助!这个我还是没搞清楚……【参考方案2】:

我希望您可以从 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,您可以轻松地使用在辅助控制器的主控制器的文本区域中输入的值!

【讨论】:

谢谢!我想我明白了,除了我的代码中最后一块东西(带有 FXMLLoader 东西的块)在哪里?在我的应用程序中,primary.fxml 在应用程序启动时加载。在用户按下按钮打开第二个窗口之前,不会加载 second.fxml。 你可以在你的openSecondWindow()中使用绑定属性! @ItachiUchiha 你能邀请我聊天吗?我对这个答案还有一些问题。

以上是关于如何从 JavaFX 中的另一个控制器类访问 UI 元素?的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX:从线程更新控制器类中的 UI 元素

如何访问 JavaFx 2.0 中的 Controller 类?

Javafx - 从 java 类更新 UI 中的进度指示器

动态访问/遍历/操作从控制器类外部的 FXML 创建的 JavaFX 节点

如何从 JavaFX 中的 UI 控件中检索阶段

如何从同一项目中的另一个类访问主类中的变量? [复制]