How to get a child node/pane from the stage? Javafx

Viewed 67

I need to get to my stackpane that is nested inside a borberpane which is also nested in another borderpane(root).

I did not create this by code, I used scene-builder/FXML.

I am now trying to access it through code. The closes I got was by using a parent and got a unmodifiable list which seems to only have its two child but I cant seem to go deeper.

Here is the Hierarchy---

<BorderPane fx:id="rootPane" focusTraversable="true" minHeight="0.0" minWidth="0.0" prefHeight="771.0" prefWidth="1100.0" stylesheets="@styling.css" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.passwordmanager2.HelloController">
   <center>
      <BorderPane fx:id="borderPane2" BorderPane.alignment="CENTER">
         <top>
            <StackPane fx:id="topBar" alignment="TOP_CENTER" focusTraversable="true" prefHeight="95.0" BorderPane.alignment="CENTER">
               <children>
                  .....

Here is main method

public class HelloApplication extends Application {


    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        double width = 900.0;
        double height = 950.0;

        Scene scene = new Scene(fxmlLoader.load(), width, height);
        Parent root = fxmlLoader.getRoot();
        System.out.println(root.getChildrenUnmodifiable().get(3)); // trying to get stackPane

        stage.setScene(scene);
        stage.initStyle(StageStyle.UNDECORATED);

        ResizeHelper.addResizeListener(stage);

        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
1 Answers

You have to create fields named the same way you specified in the .fxml file (with fx:id="..."), for each component and use the @FXML annotation.

For example you have to add @FXML private BorderPane borderPane2; to access the border pane object.

The best way to implement that is using the MVC design pattern. References:

Basically the View is the GUI (your .fxml files), the Model is the classes you use (for example you could have a Student, a Professor, and some other classes you need to implement a school score system), and the Controller is where you make changes to the model data and update the View consequently.

I'll leave here a very simple example which implements the MVC pattern:

Result:

enter image description here

Project structure:

src
 |
 +- application (package)
      |
      +-- Controller.java
      |
      +-- Main.java
      |
      +-- Test.fxml
      |
      +-- application.css

Main.java class:

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        
        try {
            FXMLLoader loader = new FXMLLoader(Main.class.getResource("ViewTest.fxml"));
            AnchorPane basePane = (AnchorPane) loader.load();
            Scene scene = new Scene(basePane);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            stage.setTitle("Test");
            stage.setScene(scene);
            stage.show();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String args[]) {
        launch(args);
    }
}

Model: Counter.java class:

package application;

public class Counter {
    private int value;
    
    public Counter() {
        this.value = 0;
    }
    
    public void increase() {
        this.value++;
    }
    public int getValue() {
        return this.value;
    }
}

Controller: Controller.java class:

package application;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public class Controller {
    @FXML
    private AnchorPane base;
    @FXML private Slider sliderTest;
    @FXML private TextField textFieldTest;
    @FXML private Button buttonTest;
    
    private Counter counter;

    public void initialize()
    {
        this.counter = new Counter();
        
        textFieldTest.setText("" + counter.getValue());
    }
    
    @FXML private void test(ActionEvent event)
    {
        this.counter.increase();
        this.textFieldTest.setText("" + counter.getValue());
    }
}

View ViewTest.fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="base" fx:id="base" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <children>
      <TextField fx:id="textFieldTest" layoutX="125.0" layoutY="187.0" />
      <Button fx:id="buttonTest" layoutX="174.0" layoutY="273.0" mnemonicParsing="false" onAction="#test" text="Increment" />
      <Slider fx:id="sliderTest" layoutX="14.0" layoutY="33.0" />
   </children>
</AnchorPane>

application.css file:

.slider {
    -fx-base: red;
}
.button {
    -fx-base: blue;
}

Another way to access a component (for example if it doesn't have a fx:id), you could start from the parent and, knowing the child node index, or iterating through them, you could get the interested node and cast it.
Example:

Node n = base.getChildren().get(0);
TextField tf = (TextField) n;
System.out.println(tf);
tf.setText("Hello JavaFX");

Output:

TextField[id=textFieldTest, styleClass=text-input text-field]

here the TextField is the 1st child of AnchorPane:

enter image description here

Related