javajavafxresizevbox

JavaFX dynamically resizes window based on the amount of VBox elements


I want to build a utility tool that vertically resizes when children are added/removed from a VBox. Children are modified via user input. The Application window is not resizable by the user.

I have created the below FXML file:

<AnchorPane fx:id="rootPane" 
            maxHeight="-Infinity" maxWidth="-Infinity" 
            minHeight="-Infinity" minWidth="-Infinity" 
            prefHeight="400.0" prefWidth="600.0" 
            xmlns="http://javafx.com/javafx/17.0.2-ea" 
            xmlns:fx="http://javafx.com/fxml/1" 
            fx:controller="myProject.controller.DynamicResizeController">
    <children>
        <VBox fx:id="vBox" 
              layoutX="148.0" layoutY="144.0" 
              prefHeight="200.0" prefWidth="200.0">
            <children>
                <Label text="This is a label" />
                <Button fx:id="addButton" 
                        onAction="#handleAddButtonClick" text="Add Element" />
                <Button fx:id="removeButton" 
                        text="Remove Element" />
            </children>
        </VBox>
    </children>
</AnchorPane>

and a controller class:

public class DynamicResizeController {

    @FXML
    private VBox vBox;

    @FXML
    private AnchorPane rootPane;

    public void handleAddButtonClick(ActionEvent event) {
        Label label = new Label("New Element added!");
        vBox.getChildren().add(label);
        calculateAndResize();
    }

    private void calculateAndResize() {
        double totalHeight = 0;
        for (Node child : vBox.getChildren()) {
            totalHeight += child.prefHeight(-1) + child.getLayoutY();
        }

        resizeStage(totalHeight);
    }

    private void resizeStage(double height) {
        // set height of stage to height
    }

}

The main class is pretty basic:

public class DynamicResizeApplication extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader fxmlLoader = new FXMLLoader(DynamicResizeApplication.class.getResource("view/dynamic-resize-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load());

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

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

}

How do I set the height of my stage? I did not find a way to access it in the controller class.


Solution

  • +1 for what @jewelsea suggested. Indeed you need to use sizeToScene() method to let the computing done automatically and resize the window.

    Having said that, calling sizeToScene() will not always make your window compact to its contents width. You need to consider some other factors about how sizeToScene() works.

    Primarily any prefWidth/prefHeight settings will impact the sizeToScene() calculations. And along with that min/max settings also play a role.

    So my suggestion is:

    So coming to your question: I am not sure why you considered AnchorPane, but all the settings of pref/min/max are not required. Because of this, even if you use sizeToScene() your window will not resize.

    Below is the demo and code of the fxml after removing all size settings and then calling sizeToScene():

    enter image description here

    <?import javafx.scene.layout.AnchorPane?>
    <?import javafx.scene.layout.VBox?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.Button?>
    <AnchorPane fx:id="rootPane" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="myProject.controller.DynamicResizeController">
        <children>
            <VBox fx:id="vBox" layoutX="148.0" layoutY="144.0" >
                <children>
                    <Label text="This is a label" />
                    <Button fx:id="addButton" onAction="#handleAddButtonClick" text="Add Element" />
                    <Button fx:id="removeButton" text="Remove Element" />
                </children>
            </VBox>
        </children>
    </AnchorPane>
    

    And your controller code:

    public void handleAddButtonClick(ActionEvent event) {
      Label label = new Label("New Element added!");
      vBox.getChildren().add(label);
      vBox.getScene().getWindow().sizeToScene();
    }