Search code examples
javajavafxfxml

how do I get an element in JavaFx using an id?


I am new to FXML and I am trying to create a handler for all of the button clicks using a switch. However, in order to do so, I need to get the elements using and id. I have tried the following but for some reason (maybe because I am doing it in the controller class and not on the main) I get a stack overflow exception.

public class ViewController {
    public Button exitBtn;

    public ViewController() throws IOException {
         Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
         Scene scene = new Scene(root);

         exitBtn = (Button) scene.lookup("#exitBtn");
    }
}

So how will I get an element (for example a button) using it's id as a reference?

The fxml block for the button is:

<Button fx:id="exitBtn" contentDisplay="CENTER" mnemonicParsing="false"
        onAction="#handleButtonClick" text="Exit" HBox.hgrow="NEVER" HBox.margin="$x1"/>

Solution

  • Use a controller class, so that you don't need to use a lookup. The FXMLLoader will inject the fields into the controller for you. The injection is guaranteed to happen before the initialize() method (if you have one) is called

    public class ViewController {
    
        @FXML
        private Button exitBtn ;
    
        @FXML
        private Button openBtn ;
    
        public void initialize() {
            // initialization here, if needed...
        }
    
        @FXML
        private void handleButtonClick(ActionEvent event) {
            // I really don't recommend using a single handler like this,
            // but it will work
            if (event.getSource() == exitBtn) {
                exitBtn.getScene().getWindow().hide();
            } else if (event.getSource() == openBtn) {
                // do open action...
            }
            // etc...
        }
    }
    

    Specify the controller class in the root element of your FXML:

    <!-- imports etc... -->
    <SomePane xmlns="..." fx:controller="my.package.ViewController">
    <!-- ... -->
        <Button fx:id="exitBtn" contentDisplay="CENTER" mnemonicParsing="false" onAction="#handleButtonClick" text="Exit" HBox.hgrow="NEVER" HBox.margin="$x1" />
        <Button fx:id="openBtn" contentDisplay="CENTER" mnemonicParsing="false" onAction="#handleButtonClick" text="Open" HBox.hgrow="NEVER" HBox.margin="$x1" />
    </SomePane>
    

    Finally, load the FXML from a class other than your controller class (maybe, but not necessarily, your Application class) with

    Parent root = FXMLLoader.load(getClass().getResource("path/to/fxml"));
    Scene scene = new Scene(root);   
    // etc...