javajavafxgridpane

Relative sizing in JavaFx GridPane


How can I set row / column sizes using weights on GridPane?

For example, if I wanted five rows where three were unknown fixed sizes, one took a third of the remaining space, and the other took two thirds of the remaining space (see image below), how could I achieve this?

What I want

I looked at using RowConstraints where two had row.setVgrow(Priority.ALWAYS); but this only allowed the remaining space to be shared equally.

I tried using percentages, as I saw in the document that if the sum of the widthPercent (or heightPercent) values total greater than 100, the values will be treated as weights. e.g. if 3 columns are each given a widthPercent of 50, then each will be allocated 1/3 of the gridpane's available width (50/(50+50+50)).

The issue is, when I tried this, I lost part of my application

Application missing

I presume this is due too the part of the documentation that says An application may freely mix the size-types of rows/columns (computed from content, fixed, or percentage). The percentage rows/columns will always be allocated space first based on their percentage of the gridpane's available space (size minus insets and gaps). The remaining space will be allocated to rows/columns given their minimum, preferred, and maximum sizes and grow priorities.


JavaFx MCVE

package sample;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        GridPane root = new GridPane();
        root.setGridLinesVisible(true);

        ColumnConstraints columnOneConstraints = new ColumnConstraints();
        columnOneConstraints.setHgrow(Priority.ALWAYS);
        root.getColumnConstraints().addAll(columnOneConstraints);

        RowConstraints rowOneConstraints = new RowConstraints();
        RowConstraints rowTwoConstraints = new RowConstraints();
        rowTwoConstraints.setVgrow(Priority.ALWAYS);
        rowTwoConstraints.setPercentHeight(2000);
        RowConstraints rowThreeConstraints = new RowConstraints();
        RowConstraints rowFourConstraints = new RowConstraints();
        rowFourConstraints.setVgrow(Priority.ALWAYS);
        rowFourConstraints.setPercentHeight(1000);
        RowConstraints rowFiveConstraints = new RowConstraints();
        root.getRowConstraints().addAll(
                rowOneConstraints,
                rowTwoConstraints,
                rowThreeConstraints,
                rowFourConstraints,
                rowFiveConstraints
        );

        Background red = new Background(new BackgroundFill(Color.RED, null, null));
        Background blue = new Background(new BackgroundFill(Color.BLUE, null, null));

        Label rowOne = new Label("R1: Unknown Fixed Size");
        rowOne.backgroundProperty().set(red);
        rowOne.setMaxWidth(Double.MAX_VALUE);
        rowOne.setAlignment(Pos.CENTER);
        rowOne.setTextFill(Color.WHITE);
        rowOne.setMaxHeight(Double.MAX_VALUE);
        root.add(rowOne, 0, 0, 1, 1);

        Label rowTwo = new Label("R2: Grow 2 parts of the remaining space");
        rowTwo.backgroundProperty().set(blue);
        rowTwo.setTextFill(Color.WHITE);
        rowTwo.setAlignment(Pos.CENTER);
        rowTwo.setMaxWidth(Double.MAX_VALUE);
        rowTwo.setMaxHeight(Double.MAX_VALUE);
        root.add(rowTwo, 0, 1, 1, 1);

        Label rowThree = new Label("R3: Unknown Fixed Size");
        rowThree.backgroundProperty().set(red);
        rowThree.setTextFill(Color.WHITE);
        rowThree.setAlignment(Pos.CENTER);
        rowThree.setMaxWidth(Double.MAX_VALUE);
        rowThree.setMaxHeight(Double.MAX_VALUE);
        root.add(rowThree, 0, 2, 1, 1);

        Label rowFour = new Label("R4: Grow 1 part of the remaining space");
        rowFour.backgroundProperty().set(blue);
        rowFour.setTextFill(Color.WHITE);
        rowFour.setAlignment(Pos.CENTER);
        rowFour.setMaxWidth(Double.MAX_VALUE);
        rowFour.setMaxHeight(Double.MAX_VALUE);
        root.add(rowFour, 0, 3, 1, 1);

        Label rowFive = new Label("R5: Unknown Fixed Size");
        rowFive.backgroundProperty().set(red);
        rowFive.setTextFill(Color.WHITE);
        rowFive.setAlignment(Pos.CENTER);
        rowFive.setMaxWidth(Double.MAX_VALUE);
        rowFive.setMaxHeight(Double.MAX_VALUE);
        root.add(rowFive, 0, 4, 1, 1);

        primaryStage.setScene(new Scene(root));
        primaryStage.sizeToScene();
        primaryStage.show();
        primaryStage.setMinHeight(primaryStage.getHeight());
        primaryStage.setMinWidth(primaryStage.getWidth());
    }
}

What I'm after could be produced with Swing's GridBagLayout, but obviously this can't be used with GridPane.

package sample;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

public class Main {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel(new GridBagLayout());

        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.BOTH;
        constraints.weightx = 1;

        JLabel rowOne = new JLabel("R1: Unknown Fixed Size", SwingConstants.CENTER);
        rowOne.setBackground(Color.RED);
        rowOne.setForeground(Color.WHITE);
        rowOne.setOpaque(true);
        constraints.gridy = 0;
        constraints.weighty = 0;
        panel.add(rowOne, constraints);

        JLabel rowTwo = new JLabel("R2: Grow 2 parts of remaining space", SwingConstants.CENTER);
        rowTwo.setBackground(Color.BLUE);
        rowTwo.setForeground(Color.WHITE);
        rowTwo.setOpaque(true);
        constraints.gridy = 1;
        constraints.weighty = 2;
        panel.add(rowTwo, constraints);

        JLabel rowThree = new JLabel("R3: Unknown Fixed Size", SwingConstants.CENTER);
        rowThree.setBackground(Color.RED);
        rowThree.setForeground(Color.WHITE);
        rowThree.setOpaque(true);
        constraints.gridy = 2;
        constraints.weighty = 0;
        panel.add(rowThree, constraints);

        JLabel rowFour = new JLabel("R4: Grow 1 part of the remaining space", SwingConstants.CENTER);
        rowFour.setBackground(Color.BLUE);
        rowFour.setForeground(Color.WHITE);
        rowFour.setOpaque(true);
        constraints.gridy = 3;
        constraints.weighty = 1;
        panel.add(rowFour, constraints);

        JLabel rowFive = new JLabel("R5: Unknown Fixed Size", SwingConstants.CENTER);
        rowFive.setBackground(Color.RED);
        rowFive.setForeground(Color.WHITE);
        rowFive.setOpaque(true);
        constraints.gridy = 4;
        constraints.weighty = 0;
        panel.add(rowFive, constraints);

        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setMinimumSize(frame.getSize());
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Solution

  • As far as I know it is not possible to achieve what your example shows with just the default JavaFX GridPane. The standard answer of the professionals in this forum would be: Create your own custom (GridBag like) layout. ;-P

    If you want to work with the default options given by JavaFX you could use a combination of e. g. BorderPane and GridPane. It is not exactly what your example shows, but maybe it comes close to it. You need to decide, if it is good enough for your needs.

    Here the example:

    When the first and the last row should have their fixed height values, you can use a BorderPane as the root. For the center you can have a GridPane with two rows which have percentage based values. The second row of it can then contain e. g. a BorderPane (fixed height for top node) as well and so on.

    FXML:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Label?>
    <?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?>
    
    <BorderPane prefHeight="600.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
       <top>
          <HBox alignment="CENTER" style="-fx-background-color: lightgreen;" BorderPane.alignment="CENTER">
             <children>
                <Label text="&quot;R1&quot;" />
             </children>
          </HBox>
       </top>
       <bottom>
          <HBox alignment="CENTER" style="-fx-background-color: lightgreen;" BorderPane.alignment="CENTER">
             <children>
                <Label text="&quot;R5&quot;" />
             </children>
          </HBox>
       </bottom>
       <center>
          <GridPane BorderPane.alignment="CENTER">
            <columnConstraints>
              <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" />
            </columnConstraints>
            <rowConstraints>
              <RowConstraints percentHeight="60.0" vgrow="SOMETIMES" />
              <RowConstraints percentHeight="40.0" vgrow="SOMETIMES" />
            </rowConstraints>
             <children>
                <HBox alignment="CENTER" style="-fx-background-color: tomato;">
                   <children>
                      <Label text="&quot;R2&quot;" />
                   </children>
                </HBox>
                <BorderPane GridPane.rowIndex="1">
                   <center>
                      <HBox alignment="CENTER" style="-fx-background-color: tomato;">
                         <children>
                            <Label text="&quot;R4&quot;" />
                         </children>
                      </HBox>
                   </center>
                   <top>
                      <HBox alignment="CENTER" style="-fx-background-color: lightgreen;" BorderPane.alignment="CENTER">
                         <children>
                            <Label text="&quot;R3&quot;" />
                         </children>
                      </HBox>
                   </top>
                </BorderPane>
             </children>
          </GridPane>
       </center>
    </BorderPane>
    
    

    preview