title: Dynamically add elements to a fixed-size GridPane in JavaFX
body: <p>I would like to display a grid containing a various number of rectangles in JavaFX. It is important that this grid cannot be resized. </p>
  
  <p>I chose the <code>GridPane</code> layout. I dynamically add <code>javafx.scene.shape.Rectangle</code> to it. Here's how my grid looks like with 2 rows and 4 columns.</p>
  
  <p><img src="https://i.stack.imgur.com/6rMsw.png" alt="Grid 4x2"></p>
  
  <p>Upon resizing, I would like it to keep the same overall shape, that is to say each <code>Rectangle</code> having the same size and keeping an horizontal and vertical gaps between my <code>Rectangle</code>s.</p>
  
  <p>However, here's what I get with a 4x4 grid:
  <img src="https://i.stack.imgur.com/kD6Jk.png" alt="enter image description here"></p>
  
  <p>The problems being:</p>
  
  <ul>
  <li>The last row and last column do not have the same size as the rest of the <code>Rectangle</code>s.</li>
  <li>The gaps have disappeared.</li>
  </ul>
  
  <p>Here is my code responsible for refreshing the display:</p>
  
  <pre><code>public void refreshConstraints() {
      getRowConstraints().clear();
      getColumnConstraints().clear();
  
      for (int i = 0; i &lt; nbRow; i++) {
          RowConstraints rConstraint = new RowConstraints();
          // ((nbRow - 1) * 10 / nbRow) = takes gap into account (10% of height)
          rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
          getRowConstraints().add(rConstraint);
      }
  
      for (int i = 0; i &lt; nbColumn; i++) {
          ColumnConstraints cConstraint = new ColumnConstraints();
          cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
          getColumnConstraints().add(cConstraint);
      }
  
  }
  </code></pre>
  
  <p>Using the <code>setFillWidth</code> and <code>setHgrow</code> yields no result either, the gap is kept between my <code>Rectangle</code>s, but the <code>Rectangle</code>s aren't resized and they overlap the rest of my GUI elements.</p>
  
  <hr>
  
  <p><strong>EDIT</strong>: MCVE code:</p>
  
  <pre><code>import javafx.application.Application;
  import javafx.application.Platform;
  import javafx.beans.value.ChangeListener;
  import javafx.beans.value.ObservableValue;
  import javafx.scene.Scene;
  import javafx.scene.control.Label;
  import javafx.scene.control.TextField;
  import javafx.scene.layout.BorderPane;
  import javafx.scene.layout.ColumnConstraints;
  import javafx.scene.layout.GridPane;
  import javafx.scene.layout.HBox;
  import javafx.scene.layout.RowConstraints;
  import javafx.scene.paint.Paint;
  import javafx.scene.shape.Rectangle;
  import javafx.stage.Stage;
  
  public class DynamicGrid extends Application {
  
      //Class containing grid (see below)
      private GridDisplay gridDisplay;
  
      @Override
      public void start(Stage primaryStage) {
  
          //Represents the grid with Rectangles
          gridDisplay = new GridDisplay(400, 200);
  
          //Fields to specify number of rows/columns
          TextField rowField = new TextField();
          TextField columnField = new TextField();
          //Function to set an action when text field loses focus
          buildTextFieldActions(rowField, columnField);
          HBox fields = new HBox();
          fields.getChildren().add(rowField);
          fields.getChildren().add(new Label("x"));
          fields.getChildren().add(columnField);
  
          BorderPane mainPanel = new BorderPane();
          mainPanel.setLeft(gridDisplay.getDisplay());
          mainPanel.setBottom(fields);
  
          Scene scene = new Scene(mainPanel);
          primaryStage.setTitle("Test grid display");
          primaryStage.setScene(scene);
          primaryStage.show();
      }
  
      /**
       * @param args the command line arguments
       */
      public static void main(String[] args) {
          launch(args);
      }
  
      private void buildTextFieldActions(final TextField rowField, final TextField columnField) {
          rowField.focusedProperty().addListener(new ChangeListener&lt;Boolean&gt;() {
  
              @Override
              public void changed(ObservableValue&lt;? extends Boolean&gt; ov, Boolean t, Boolean t1) {
                  if (!t1) {
                      if (!rowField.getText().equals("")) {
                          try {
                              int nbRow = Integer.parseInt(rowField.getText());
                              gridDisplay.setRows(nbRow);
                              gridDisplay.updateDisplay();
                          } catch (NumberFormatException nfe) {
                              System.out.println("Please enter a valid number.");
                          }
                      }
                  }
              }
          });
  
          columnField.focusedProperty().addListener(new ChangeListener&lt;Boolean&gt;() {
  
              @Override
              public void changed(ObservableValue&lt;? extends Boolean&gt; ov, Boolean t, Boolean t1) {
                  if (!t1) {
                      if (!columnField.getText().equals("")) {
                          try {
                              int nbColumn = Integer.parseInt(columnField.getText());
                              gridDisplay.setColumns(nbColumn);
                              gridDisplay.updateDisplay();
                          } catch (NumberFormatException nfe) {
                              System.out.println("Please enter a valid number.");
                          }
                      }
                  }
              }
          });
      }
  
      //Class responsible for displaying the grid containing the Rectangles
      public class GridDisplay {
  
          private GridPane gridPane;
          private int nbRow;
          private int nbColumn;
          private int width;
          private int height;
          private double hGap;
          private double vGap;
  
          public GridDisplay(int width, int height) {
              this.gridPane = new GridPane();
              this.width = width;
              this.height = height;
              build();
          }
  
          private void build() {
              this.hGap = 0.1 * width;
              this.vGap = 0.1 * height;
              gridPane.setVgap(vGap);
              gridPane.setHgap(hGap);
              gridPane.setPrefSize(width, height);
              initializeDisplay(width, height);
          }
  
          //Builds the first display (correctly) : adds a Rectangle for the number
          //of rows and columns
          private void initializeDisplay(int width, int height) {
              nbRow = height / 100;
              nbColumn = width / 100;
  
              for (int i = 0; i &lt; nbColumn; i++) {
                  for (int j = 0; j &lt; nbRow; j++) {
                      Rectangle rectangle = new Rectangle(100, 100);
                      rectangle.setStroke(Paint.valueOf("orange"));
                      rectangle.setFill(Paint.valueOf("steelblue"));
                      gridPane.add(rectangle, i, j);
  
                  }
              }
          }
  
          //Function detailed in post
          //Called in updateDisplay()
          public void refreshConstraints() {
              gridPane.getRowConstraints().clear();
              gridPane.getColumnConstraints().clear();
              for (int i = 0; i &lt; nbRow; i++) {
                  RowConstraints rConstraint = new RowConstraints();
                  rConstraint.setPercentHeight(100 / nbRow - ((nbRow - 1) * 10 / nbRow));
                  gridPane.getRowConstraints().add(rConstraint);
              }
  
              for (int i = 0; i &lt; nbColumn; i++) {
                  ColumnConstraints cConstraint = new ColumnConstraints();
                  cConstraint.setPercentWidth(100 / nbColumn - ((nbColumn - 1) * 10 / nbColumn));
                  gridPane.getColumnConstraints().add(cConstraint);
              }
  
          }
  
          public void setColumns(int newColumns) {
              nbColumn = newColumns;
          }
  
          public void setRows(int newRows) {
              nbRow = newRows;
          }
  
          public GridPane getDisplay() {
              return gridPane;
          }
  
          //Function called when refreshing the display
          public void updateDisplay() {
              Platform.runLater(new Runnable() {
  
                  @Override
                  public void run() {
                      //The gridpane is cleared of the previous children
                      gridPane.getChildren().clear();
  
                      //A new rectangle is added for row*column
                      for (int i = 0; i &lt; nbColumn; i++) {
                          for (int j = 0; j &lt; nbRow; j++) {
                              Rectangle rectangle = new Rectangle(100, 100);
                              rectangle.setStroke(Paint.valueOf("orange"));
                              rectangle.setFill(Paint.valueOf("steelblue"));
                              gridPane.add(rectangle, i, j);
                          }
                      }
                      //Call to this function to update the grid's constraints
                      refreshConstraints();
                  }
              });
  
          }
  
      }
  
  }
  </code></pre>