Continue your JavaFX Components learning via this tutorial.

Example 1: Layout

Here are the code:

Step 1: Create Project

  1. Open your favorite Java IDE.
  2. In the menu go to File --> Create New Project.

Step 2: Dependencies

No dependencies are needed for this project.

Step 3: Write Code

Our code will comprise the following java files:

  • AutoResponsiveLayout.java
  • AutoResponsiveLayoutExample.java
  1. In your editor or IDE, create a file known as AutoResponsiveLayout.java.
  2. Then add the following code:

(a). AutoResponsiveLayout.java

Go ahead and add the following imports:

import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;

Go ahead and add the following imports:

import java.util.ArrayList;
import java.util.List;

Create our class as shown below:

public class AutoResponsiveLayout {

Our class will have the following methods:

  • void initRows()
  • void resetRows()
  • void assignWidgetsToRows()
  • void calculateTotalChildMinWidth()
  • void calculateAverageRowWidthBasedOnMinWidths()
  • void clear()
  • void addWidget(Region child)
  • void addWidget(Region child, WidgetLayoutInfo layoutInfo)
  • protected void layoutPane()
  • void adjustPaneToParentScrollPaneOrMinUsedRowsHeight()
  • void transferMinWidthsAndHeightsToWidths()
  • void pullUpWidgets(ObservableList<Node> children, List<WidgetLayoutInfo> widgetLayoutInfos)
  • void expandWidgetWidthsToFitRow(double newWidth, ObservableList<Node> children)
  • void expandRowHeights()
  • void printWidgetRows(ObservableList<Node> children)
  • void assignChildrenToRowNumbersAccordingToMinWidths()
  • void assignChildrenToRowsUsingAverageRowWidth()
  • void positionAndSizeChildren(ObservableList<Node> children, List<WidgetLayoutInfo> widgetLayoutInfos)

Here is the full code:

package com.jenkov.javafx.layout;

import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;

import java.util.ArrayList;
import java.util.List;

/**
 * An example of how to implement your own layout of controls contained inside a Pane. The JavaFX Pane class does not
 * do any layout of its children. The Pane shows the children where the children wants to be layed out themselves.
 * Thus, by changing layout position of the children of a Pane you can change it's layout.
 *
 * Future potential improvements:
 * - Remodel the code so it more clearly shows the algorithm - even though it may slow the algorithm down a bit.
 * - Max widths on widgets
 * - Vertical stacking of widgets
 * - Confine widgets to fixed row number
 *
 */
public class AutoResponsiveLayout {

    public static class WidgetLayoutInfo {

        public double x = 0.0D;
        public double y = 0.0D;

        public double minWidth  = 0.0D;
        public double width     = 0.0D;

        public double minHeight = 0.0D;
        public double height    = 0.0D;

        public int   rowNo = 0;
    }

    public static class RowLayoutInfo {
        public int    rowNo    = 0;
        public double rowWidth = 0.0D;

        public List<WidgetLayoutInfo> widgets = new ArrayList<>();
    }

    public static class PaneLayoutInfo {

        public List<WidgetLayoutInfo> widgetLayoutInfos = new ArrayList<>();
        public List<RowLayoutInfo>    rowLayoutInfos    = new ArrayList<>();

        public int    noOfRows    = 0;
        public double maxRowWidth = 0.0D;

        public double totalChildWidth = 0.0D; //width of all children if placed on a single row
        public double avgRowWidth     = 0.0D;  // total child width divided by number of rows.
        public double visibleHeight   = 0.0D; // The height visible within the parent ScrollPane

        public void initRows() {
            for(int i = this.rowLayoutInfos.size(); i <= noOfRows; i++) {
                RowLayoutInfo rowLayoutInfo = new RowLayoutInfo();
                rowLayoutInfo.rowNo = i;
                this.rowLayoutInfos.add(rowLayoutInfo);
            }
        }

        public void resetRows() {
            for(int i=0; i<this.rowLayoutInfos.size(); i++) {
                RowLayoutInfo rowLayoutInfo = this.rowLayoutInfos.get(i);
                rowLayoutInfo.widgets.clear();
            }
        }

        public void assignWidgetsToRows() {
            resetRows();
            for(int i=0; i<widgetLayoutInfos.size(); i++) {
                WidgetLayoutInfo widgetLayoutInfo = widgetLayoutInfos.get(i);
                RowLayoutInfo rowLayoutInfo = rowLayoutInfos.get(widgetLayoutInfo.rowNo);
                rowLayoutInfo.widgets.add(widgetLayoutInfo);
            }
        }

        public int determineRowCountFromWidgetLayoutInfos() {
            this.noOfRows = getLastWidgetLayoutInfo(this.widgetLayoutInfos).rowNo + 1; // row numbers are 0-based (first row has index 0)
            return this.noOfRows;
        }

        private WidgetLayoutInfo getLastWidgetLayoutInfo(List<WidgetLayoutInfo> widgetLayoutInfos) {
            return widgetLayoutInfos.get(widgetLayoutInfos.size()-1);
        }

        public void calculateTotalChildMinWidth(){
            this.totalChildWidth = 0.0D;
            for(int i=0; i<this.widgetLayoutInfos.size(); i++) {
                WidgetLayoutInfo widgetLayoutInfo = this.widgetLayoutInfos.get(i);
                double childWidth = widgetLayoutInfo.minWidth;
                this.totalChildWidth += childWidth;
            }
        }

        public void calculateAverageRowWidthBasedOnMinWidths() {
            this.avgRowWidth = this.totalChildWidth / this.noOfRows;
        }

        private double calcMinimumUsedRowHeights() {
            int rowEndIndex = 0;
            double minimumUsedRowHeights = 0.0D;
            for(int rowNo = 0; rowNo < this.noOfRows; rowNo++){
                double highestWidgetsOnRow = 0.0D;
                while(rowEndIndex < this.widgetLayoutInfos.size() && rowNo == this.widgetLayoutInfos.get(rowEndIndex).rowNo) {
                    WidgetLayoutInfo widgetLayoutInfo = this.widgetLayoutInfos.get(rowEndIndex);
                    highestWidgetsOnRow = Math.max(highestWidgetsOnRow, widgetLayoutInfo.minHeight);
                    rowEndIndex++;
                }
                minimumUsedRowHeights += highestWidgetsOnRow;
            }
            return minimumUsedRowHeights;
        }

    }

    // Feature flags. All features should be enabled for a fully automatic responsive layout

    protected boolean balanceRows       = true;
    protected boolean pullUpChildren    = true;
    protected boolean extendChildWidth  = true;
    protected boolean extendChildHeight = true;

    PaneLayoutInfo          paneLayoutInfo = new PaneLayoutInfo();

    private Pane targetPane = null;
    private ScrollPane targetParentPane = null;

    public AutoResponsiveLayout(Pane pane, ScrollPane parent) {
        this.targetPane = pane;
        this.targetParentPane = parent;

        this.targetPane.widthProperty().addListener((ObservableValue<? extends Number> property, Number oldValue, Number newValue) -> {
            this.paneLayoutInfo.maxRowWidth = newValue.doubleValue();
            layoutPane();
        });

        parent.viewportBoundsProperty().addListener((ObservableValue<? extends Bounds> property, Bounds oldValue, Bounds newValue) -> {
            this.paneLayoutInfo.visibleHeight = newValue.getHeight();
            layoutPane();
        });

    }

    public void clear() {
        //this.paneLayoutInfo.resetRows();
        this.paneLayoutInfo.widgetLayoutInfos.clear();
        this.targetPane.getChildren().clear();
    }

    public void addWidget(Region child) {
        WidgetLayoutInfo widgetLayoutInfo = new WidgetLayoutInfo();
        widgetLayoutInfo.x        = 0.0D;
        widgetLayoutInfo.y        = 0.0D;
        widgetLayoutInfo.rowNo    = 0;
        widgetLayoutInfo.minWidth = child.getMinWidth();
        widgetLayoutInfo.width    = child.getMinWidth(); //start with min width as width
        widgetLayoutInfo.minHeight= child.getMinHeight();
        widgetLayoutInfo.height   = child.getMinWidth();

        addWidget(child, widgetLayoutInfo);
    }

    public void addWidget(Region child, WidgetLayoutInfo layoutInfo) {
        this.targetPane.getChildren().add(child);
        this.paneLayoutInfo.widgetLayoutInfos.add(layoutInfo);

    }

    protected void layoutPane() {
        //System.out.println("============== BEGIN LAYOUT ===============");
        ObservableList<Node> children = this.targetPane.getChildren();
        if(children.size() == 0) {
            return;
        }

        transferMinWidthsAndHeightsToWidths();

        // Phase 1: Divide children into rows based on their minimum widths
        assignChildrenToRowNumbersAccordingToMinWidths();

        this.paneLayoutInfo.determineRowCountFromWidgetLayoutInfos();
        //this.paneLayoutInfo.initRows();
        //this.paneLayoutInfo.resetRows();
        //this.paneLayoutInfo.assignWidgetsToRows();

        adjustPaneToParentScrollPaneOrMinUsedRowsHeight();

        // Phase 2.1: Now that number of rows is decided, calculate the average width of rows
        paneLayoutInfo.calculateTotalChildMinWidth();
        paneLayoutInfo.calculateAverageRowWidthBasedOnMinWidths();

        // Phase 2.2: Assign children to rows based on average row width instead of max row width - for a more even distribution of children.
        if(this.balanceRows) {
            assignChildrenToRowsUsingAverageRowWidth();
        }

        // Phase 2.3 Pull up "dangling" widgets towards the top of the pane, so the grid looks more similar.
        if(this.pullUpChildren) {
            pullUpWidgets(children, this.paneLayoutInfo.widgetLayoutInfos);
        }

        // Phase 3: Expand widths of children to match row width
        if(this.extendChildWidth) {
            expandWidgetWidthsToFitRow(this.paneLayoutInfo.maxRowWidth, children);
        }

        // Phase 4: Expand heights of children to match highest child per row
        if(this.extendChildHeight) {
            expandRowHeights();
        }

        // Phase 5: Position children according to index and row
        positionAndSizeChildren(children, this.paneLayoutInfo.widgetLayoutInfos);

    }

    private int determineRowCountFromWidgetLayoutInfos() {
        return getLastWidgetLayoutInfo(this.paneLayoutInfo.widgetLayoutInfos).rowNo + 1; // row numbers are 0-based (first row has index 0)
    }

    private void adjustPaneToParentScrollPaneOrMinUsedRowsHeight() {
        double minimumUsedRowHeights = this.paneLayoutInfo.calcMinimumUsedRowHeights();
        double newHeight = Math.max(this.paneLayoutInfo.visibleHeight, minimumUsedRowHeights);
        this.targetPane.setMinHeight(newHeight);
        this.targetPane.setPrefHeight(newHeight);
        this.targetPane.setMaxHeight(newHeight);
    }

    private void transferMinWidthsAndHeightsToWidths() {
        for(int i=0; i<this.paneLayoutInfo.widgetLayoutInfos.size(); i++){
            WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);
            widgetLayoutInfo.width  = widgetLayoutInfo.minWidth;
            widgetLayoutInfo.height = widgetLayoutInfo.minHeight;
        }
    }

    private void pullUpWidgets(ObservableList<Node> children, List<WidgetLayoutInfo> widgetLayoutInfos) {
        int    childrenPulledUpThisIteration = 0;
        do{
            childrenPulledUpThisIteration = 0;
            //System.out.println("===== Pull up round =====");
            double prevRowWidth = Double.MAX_VALUE;
            double thisRowWidth = 0.0D;
            int    thisRowNo    = 0;

            //Node firstChildOnRow = children.get(0);
            WidgetLayoutInfo firstChildOnRowWidgetLayoutInfo = widgetLayoutInfos.get(0);
            for(int i = 0; i < children.size(); i++) {
                WidgetLayoutInfo widgetLayoutInfo = widgetLayoutInfos.get(i);
                if(thisRowNo == widgetLayoutInfo.rowNo) {
                    thisRowWidth += widgetLayoutInfo.minWidth;
                } else {
                    double rowDiff = thisRowWidth - prevRowWidth;
                    //if(rowDiff >= firstChildOnRow.getLayoutBounds().getWidth()) {
                    if(rowDiff >= firstChildOnRowWidgetLayoutInfo.minWidth) {
                        //System.out.println("Move child " + i + " up to previous row");
                        firstChildOnRowWidgetLayoutInfo.rowNo--; //move first widget of this row up to previous row
                        childrenPulledUpThisIteration++;
                    } else {
                        //System.out.println("Row diff for row " + thisRowNo);
                    }

                    //firstChildOnRow = child;
                    firstChildOnRowWidgetLayoutInfo = widgetLayoutInfo;
                    prevRowWidth = thisRowWidth;
                    //thisRowWidth = child.getLayoutBounds().getWidth();
                    thisRowWidth = widgetLayoutInfo.minWidth;
                    thisRowNo++;
                }
            }
        } while (childrenPulledUpThisIteration > 0);
    }

    private void expandWidgetWidthsToFitRow(double newWidth, ObservableList<Node> children) {
        double usedRowWidth = 0.0D;

        int rowStartIndex = 0;
        int rowEndIndex   = 0;
        for(int rowNo = 0; rowNo < paneLayoutInfo.noOfRows; rowNo++){

            while(rowEndIndex < this.paneLayoutInfo.widgetLayoutInfos.size() && rowNo == this.paneLayoutInfo.widgetLayoutInfos.get(rowEndIndex).rowNo){
                rowEndIndex++;
            }
            for(int i=rowStartIndex; i<rowEndIndex; i++) {
                //Node node = children.get(i);
                //usedRowWidth += node.getLayoutBounds().getWidth();
                WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);
                usedRowWidth += widgetLayoutInfo.minWidth;
            }

            //System.out.println("   Max Row Width : " + newWidth + "(" + paneLayoutInfo.maxRowWidth + ")");
            //System.out.println("   Used Row width: " + usedRowWidth);

            double unusedRowWidth = paneLayoutInfo.maxRowWidth - usedRowWidth;
            //System.out.println("   Unused row width: " + unusedRowWidth);

            for(int i=rowStartIndex; i<rowEndIndex; i++) {
                Node node = children.get(i);

                WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);
                double childWidth      = widgetLayoutInfo.minWidth;
                double childToRowRatio = childWidth / usedRowWidth;
                double childWidthExtension = unusedRowWidth * childToRowRatio;
                double childWidthExtended = childWidth + childWidthExtension;

                //System.out.println("      Child Width Extension: " + childWidthExtension);
                //System.out.println("      Child Width Extended : " + childWidthExtended);

                widgetLayoutInfo.width = childWidthExtended;
                //((Region) node).setMinWidth(childWidthExtended);
                //((Region) node).setPrefWidth(childWidthExtended);
            }

            usedRowWidth = 0.0D;
            rowStartIndex = rowEndIndex;
        }
    }

    private void expandRowHeights() {
        double minimumUsedRowHeights = this.paneLayoutInfo.calcMinimumUsedRowHeights();

        double unusedHeight = 0.0D;
        //if(this.paneLayoutInfo.visibleHeight > this.targetPane.getHeight()) {
        if(this.paneLayoutInfo.visibleHeight > minimumUsedRowHeights) {
            //unusedHeight = Math.max(0, this.targetPane.getHeight() - minimumUsedRowHeights);
            unusedHeight = Math.max(0, this.paneLayoutInfo.visibleHeight - minimumUsedRowHeights);
        }

        //if minimumUsedRowHeights is larger than available height - do NOT use a negative unusedHeight - but use 0.

        //System.out.println("Pane height        : " + this.targetPane.getHeight());
        //System.out.println("Visible height     : " + this.paneLayoutInfo.visibleHeight);
        //System.out.println("Minimum rows height: " + minimumUsedRowHeights);
        //System.out.println("Unused height      : " + unusedHeight);

        int rowStartIndex = 0;
        int rowEndIndex   = 0;
        for(int rowNo = 0; rowNo < paneLayoutInfo.noOfRows; rowNo++){
            rowStartIndex = rowEndIndex;
            double highestWidgetsOnRow = 0.0D;
            while(rowEndIndex < this.paneLayoutInfo.widgetLayoutInfos.size() && rowNo == this.paneLayoutInfo.widgetLayoutInfos.get(rowEndIndex).rowNo) {
                WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(rowEndIndex);
                highestWidgetsOnRow = Math.max(highestWidgetsOnRow, widgetLayoutInfo.height);
                rowEndIndex++;
            }
            double rowRatio       = highestWidgetsOnRow / minimumUsedRowHeights;
            double rowExtension   = rowRatio * unusedHeight;
            double expandedHeight = highestWidgetsOnRow + rowExtension;
            //System.out.println("Row Height     : " + highestWidgetsOnRow);
            //System.out.println("Unused height  : " + unusedHeight);
            //System.out.println("Expanded height: " + expandedHeight);

            rowEndIndex = rowStartIndex;
            while(rowEndIndex < this.paneLayoutInfo.widgetLayoutInfos.size() && rowNo == this.paneLayoutInfo.widgetLayoutInfos.get(rowEndIndex).rowNo) {
                WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(rowEndIndex);
                widgetLayoutInfo.height = expandedHeight;
                rowEndIndex++;
            }

        }
    }

    private void printWidgetRows(ObservableList<Node> children) {
        System.out.println("=== Widget Row Nos ===");
        for(int i = 0; i< children.size(); i++) {
            WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);
            System.out.println("Widget " + i + " has row " + widgetLayoutInfo.rowNo);
        }
    }

    private WidgetLayoutInfo getLastWidgetLayoutInfo(List<WidgetLayoutInfo> widgetLayoutInfos) {
        return widgetLayoutInfos.get(widgetLayoutInfos.size()-1);
    }

    private void assignChildrenToRowNumbersAccordingToMinWidths() {
        ObservableList<Node> children = targetPane.getChildren();
        double maxRowWidth = paneLayoutInfo.maxRowWidth;

        double widgetsOnRowWidth = 0.0D;
        int rowNo = 0;
        for(int i = 0; i < children.size(); i++) {
            WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);

            double childWidth =  widgetLayoutInfo.minWidth;
            widgetsOnRowWidth += childWidth;
            if(widgetsOnRowWidth > maxRowWidth && i>0) {
                rowNo++;
                widgetsOnRowWidth = childWidth;
            }
            widgetLayoutInfo.rowNo = rowNo;
        }
    }

    private void assignChildrenToRowsUsingAverageRowWidth() {
        //System.out.println("Avg. row width: " + avgRowWidth);
        double avgRowWidth = paneLayoutInfo.avgRowWidth;
        double maxRowWidth = paneLayoutInfo.maxRowWidth;
        double totalWidgetsWidthUsed = 0.0D;
        double widgetWidthOnRowUsed  = 0.0D;
        int rowNo = 0;
        for(int i = 0; i < this.paneLayoutInfo.widgetLayoutInfos.size(); i++) {
            WidgetLayoutInfo widgetLayoutInfo = this.paneLayoutInfo.widgetLayoutInfos.get(i);
            double childWidth = widgetLayoutInfo.minWidth;

            widgetWidthOnRowUsed  += childWidth;
            totalWidgetsWidthUsed += childWidth;

            double avgRowWidthSum = avgRowWidth * ((double) (rowNo + 1) );

            if(widgetWidthOnRowUsed > maxRowWidth){
                // widget cannot fit within this row - push to next row.
                widgetWidthOnRowUsed = childWidth;
                rowNo++;
                widgetLayoutInfo.rowNo = rowNo;
            }
            if(totalWidgetsWidthUsed >= avgRowWidthSum) {
                // widget can fit within this row - but after this widget break to next row
                widgetLayoutInfo.rowNo = rowNo;
                rowNo++;
                widgetWidthOnRowUsed = 0.0D;
            } else {
                widgetLayoutInfo.rowNo = rowNo;
            }
        }
    }

    private void positionAndSizeChildren(ObservableList<Node> children, List<WidgetLayoutInfo> widgetLayoutInfos) {
        int rowNo = 0;
        double x = 0.0D;
        double y = 0.0D;
        for(int i = 0; i< children.size(); i++) {
            Region           child            = (Region) children.get(i);
            WidgetLayoutInfo widgetLayoutInfo = widgetLayoutInfos.get(i);

            if(rowNo != widgetLayoutInfo.rowNo) {

                double highestWidgetOnRow = 0.0D;
                System.out.print("Calculating height for row: " + rowNo);
                for(int j=i-1; j>=0; j--) {
                    WidgetLayoutInfo widgetOnRow = widgetLayoutInfos.get(j);
                    if(widgetOnRow.rowNo != rowNo) { //reached previous row - skip loop now.
                        break;
                    }
                    //highestWidgetOnRow = Math.max(highestWidgetOnRow, widgetOnRow.minHeight);
                    highestWidgetOnRow = Math.max(highestWidgetOnRow, widgetOnRow.height);
                }
                System.out.println(" => " + highestWidgetOnRow);

                rowNo = widgetLayoutInfo.rowNo;
                x = 0.0D;

                y += highestWidgetOnRow;
            }

            child.setLayoutX(x);
            child.setLayoutY(y);
            child.setPrefWidth(widgetLayoutInfo.width);
            child.setPrefHeight(widgetLayoutInfo.height);

            x += widgetLayoutInfo.width;
        }
    }

}
  1. Next create another file known as AutoResponsiveLayoutExample.java.
  2. And add the following code:

(b). AutoResponsiveLayoutExample.java

We will start by adding some imports to this class:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.shape.StrokeType;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

We will start by adding some imports to this class:

import java.util.List;

Through inheritance we will be able to derive properties from a parent class. However we have to extend that parent class. So we do that using the extends keyword.

public class AutoResponsiveLayoutExample extends Application {

Our class will have the following methods:

  • void main(String[] args)
  • void start(Stage primaryStage)
  • void addButton(AutoResponsiveLayout autoResponsiveLayout, ToolBar toolbar, String buttonText, double minWidth, double minHeight)

All Java applications have an entry point known as the main method. We will define it:

    public static void main(String[] args) {

Here is the full code:

package com.jenkov.javafx.layout;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.shape.StrokeType;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

import java.util.List;

public class AutoResponsiveLayoutExample extends Application {

    private int widgetCount = 0;

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

    public void start(Stage primaryStage) {
        ScrollPane scrollPane = new ScrollPane();

        scrollPane.pannableProperty().set(true);
        scrollPane.fitToWidthProperty().set(true);
        //scrollPane.fitToHeightProperty().set(true);

        //scrollPane.setPrefHeight(1024);
        scrollPane.setPrefHeight(4096);

        scrollPane.hbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        scrollPane.vbarPolicyProperty().setValue(ScrollPane.ScrollBarPolicy.AS_NEEDED);

        Pane containerPane = new Pane();
        scrollPane.setContent(containerPane);
        AutoResponsiveLayout autoResponsiveLayout = new AutoResponsiveLayout(containerPane, scrollPane);

        ToolBar toolbar = createToolBar(containerPane, autoResponsiveLayout);

        //VBox vBox = new VBox(toolbar, containerPane);
        VBox vBox = new VBox(toolbar, scrollPane);
        Scene scene = new Scene(vBox);
        primaryStage.setScene(scene);

        primaryStage.setWidth(1024);
        primaryStage.setHeight(800);
        primaryStage.setTitle("Auto-responsive Layout Example");

        primaryStage.show();
    }

    private ToolBar createToolBar(Pane containerPane, AutoResponsiveLayout autoResponsiveLayout) {
        ToolBar toolbar = new ToolBar();

        addButton(autoResponsiveLayout, toolbar, "1x1", 256, 256);
        addButton(autoResponsiveLayout, toolbar, "2x1", 512, 256);
        addButton(autoResponsiveLayout, toolbar, "1x2", 256, 512);
        addButton(autoResponsiveLayout, toolbar, "2x2", 512, 512);

        toolbar.getItems().add(new Separator());

        ToggleButton toggleButton2 = new ToggleButton("Balance Rows");
        toggleButton2.setOnAction((event) -> {
            autoResponsiveLayout.balanceRows = !autoResponsiveLayout.balanceRows;
            autoResponsiveLayout.layoutPane();
        });

        toolbar.getItems().add(toggleButton2);

        ToggleButton toggleButton1 = new ToggleButton("Pull Up Children");
        toggleButton1.setOnAction((event) -> {
            autoResponsiveLayout.pullUpChildren = !autoResponsiveLayout.pullUpChildren;
            autoResponsiveLayout.layoutPane();
        });

        toolbar.getItems().add(toggleButton1);

        ToggleButton toggleButton3 = new ToggleButton("Expand Child Width");
        toggleButton3.setOnAction((event) -> {
            autoResponsiveLayout.extendChildWidth = !autoResponsiveLayout.extendChildWidth;
            autoResponsiveLayout.layoutPane();
        });

        toolbar.getItems().add(toggleButton3);

        ToggleButton toggleButton4 = new ToggleButton("Expand Child Height");
        toggleButton4.setOnAction((event) -> {
            autoResponsiveLayout.extendChildHeight = !autoResponsiveLayout.extendChildHeight;
            autoResponsiveLayout.layoutPane();
        });

        toolbar.getItems().add(toggleButton4);

        toolbar.getItems().add(new Separator());

        ToggleButton toggleButton5 = new ToggleButton("Clear");
        toggleButton5.setOnAction((event) -> {
            autoResponsiveLayout.clear();
            autoResponsiveLayout.layoutPane();
        });
        toolbar.getItems().add(toggleButton5);

        return toolbar;
    }

    private void addButton(AutoResponsiveLayout autoResponsiveLayout, ToolBar toolbar, String buttonText, double minWidth, double minHeight) {
        Button buttonAdd = new Button(buttonText);

        buttonAdd.setOnAction((event) -> {
            Pane widgetPane = createWidgetPane(minWidth, minHeight);
            autoResponsiveLayout.addWidget(widgetPane);
            autoResponsiveLayout.layoutPane();
        });

        toolbar.getItems().add(buttonAdd);
    }

    private Pane createWidgetPane(double minWidth, double minHeight) {
        this.widgetCount++;

        Pane widgetPane = new Pane();
        widgetPane.setMinWidth(minWidth);
        widgetPane.setMinHeight(minHeight);

        StrokeType     strokeType     = StrokeType.INSIDE;
        StrokeLineJoin strokeLineJoin = StrokeLineJoin.MITER;
        StrokeLineCap  strokeLineCap  = StrokeLineCap.BUTT;
        double         miterLimit     = 10;
        double         dashOffset     = 0;
        List<Double> dashArray      = null;

        BorderStrokeStyle borderStrokeStyle =
                new BorderStrokeStyle(
                        strokeType,
                        strokeLineJoin,
                        strokeLineCap,
                        miterLimit,
                        dashOffset,
                        dashArray
                );

        BorderStroke borderStroke =
                new BorderStroke(
                        //Color.valueOf("08ff80"),
                        Color.valueOf("#303F9F"),
                        borderStrokeStyle,
                        new CornerRadii(0),
                        new BorderWidths(8)
                );

        Border border = new Border(borderStroke);

        Label label = new Label("" + this.widgetCount);
        label.setFont(Font.font("Arial", FontWeight.BOLD , FontPosture.REGULAR, 32));
        label.setLayoutX(20);
        label.setLayoutY(20);
        widgetPane.getChildren().add(label);
        widgetPane.setBorder(border);
        return widgetPane;
    }
}

Download

Download the code using the below links:

Number Link
1. Download Example
2. Follow code author
3. Code: Apache 2.0 License