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

后端 未结 2 1150
走了就别回头了
走了就别回头了 2020-12-01 13:21

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

My application has a main window that has its own FXML file (primary.fxm

相关标签:
2条回答
  • 2020-12-01 13:27

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

    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.

    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 load function, which is useless here.

    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.load method 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)).

    Your primary controller has a field:

    @FXML private TextArea myArea;
    

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

    In your secondary controller, add this field:

    public TextArea primaryTextArea;
    

    Note that @FXML is 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:

    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 load function, which is useless here.

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

    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.

    0 讨论(0)
  • 2020-12-01 13:38

    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.

    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.

    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

    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 text of both the controllers and you can easily use the value that is entered in the textarea of the primary controller in your secondary controller !

    0 讨论(0)
提交回复
热议问题