如何从 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;
注意@FXML
和private
都消失了(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 2.0 中的 Controller 类?
Javafx - 从 java 类更新 UI 中的进度指示器