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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21995359/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:55:12  来源:igfitidea点击:

How do I access a UI element from another controller class in JavaFX?

javajavafxjava-8fxml

提问by adeena

I have a JavaFX / Java 8 application written with NetBeans 8 (no SceneBuilder).

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

My application has a main window that has its own FXML file (primary.fxml) and its own controller class (FXMLPrimaryController.java). One of the items in the FXML is a TextArea. Some of the methods in FXMLPrimaryController.java are about appending to that TextArea.

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

This application now spawns a second window (another "stage") with its own FXML (second.fxml) and its own controller class (FXMLsecondController.java).

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

Within the second controller class, how can I access the TextArea in the primary?

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

Here's a sample of the relevant code:

这是相关代码的示例:

primary.fxml:

主要.fxml:

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

FXMLPrimaryController.java:

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

}

There is nothing fancy about second.fxml. Assume there is a button with onAction="#writeSomething".

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

In FXMLsecondController.java, I would like a function that references the above TextArea.

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

回答by Rainer Schwarze

When you load the FXML for the secondary controller, do this:

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

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

Then you can pass references to the second controller. This could just be the TextArea.

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

EDIT:

编辑:

Replaced the load()call in the snippet above and added the setLocation()call. The old line AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));was wrong as it called the static loadfunction, which is useless here.

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

EDIT:

编辑:

(Changed the code snippet above to better match your variable names.) The code snippet above replaces this part of your code:

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

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

That line uses the FXMLLoader to load the view and also creates the instance of the controller - in this case FXMLSecondaryController. However, you cannot use the static FXMLLoader.loadmethod for that, you need an instance of FXMLLoader. That instance holds a reference to the controller after load and you can retrieve that with getController(). And you need to cast the returned value to your controller class (with (FXMLSecondaryController)).

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

Your primary controller has a field:

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

@FXML private TextArea myArea;

This holds a reference to your TextAreaand is initialized by the fxml loader. (If you remove the @FXMLannotation, the loader won't touch it.)

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

In your secondary controller, add this field:

在您的辅助控制器中,添加此字段:

public TextArea primaryTextArea;

Note that @FXMLis gone (the fxml loader shall not touch the field) as well as private(you want to set the field, hence others must see it). Now you can set this field after the controller has been loaded. So back to the loading code:

请注意,@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;

EDIT: Replaced the load()call in the snippet above and added the setLocation()call. The old line AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));was wrong as it called the static loadfunction, which is useless here.

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

The FXMLSecondaryControllernow has a reference to the TextAreaof the primary controller. In one of its methods you should be able to access its content:

FXMLSecondaryController现在具有与一个参考TextArea主控制器。在其中一种方法中,您应该能够访问其内容:

public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

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

}

Note that this solution is not the best approach, but it is simple and can serve as a start. Using binding is certainly recommended. I would try to create a class which holds the data and on which the other controllers bind. But if you take the simple approach for now, it should be sufficient.

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

回答by ItachiUchiha

I hope you could expose the TextField from the FXMLPrimaryController, and have a TextField property in the FXMLsecondController; then set that property when you assemble the pieces together. This would not be a good design, though, because you would be exposing details of the view through the controllers.

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

I am sure you don't want the TextArea but the data of the TextArea in second.fxml. So what I would suggest is to expose a String property in each of the controllers and bind them together.

我确定您不想要 TextArea 而是 .txt 中的 TextArea 数据second.fxml。所以我的建议是在每个控制器中公开一个 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();
}
}

and then in your FXMLsecondController, you can have

然后在你的 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();  
     }  
}

Then you can bind these two, using something like this

然后你可以绑定这两个,使用这样的东西

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

This now binds the variable textof both the controllers and you can easily use the value that is entered in the textarea of the primary controller in your secondary controller !

这现在绑定了text两个控制器的变量,您可以轻松地使用在辅助控制器中主控制器文本区域中输入的值!