JavaFX 2.0 Layout Panes – GridPane
GridPane
is without a doubt the most powerfull and flexible layout pane in JavaFX 2.0.It lays out its children in a flexible grid of columns and rows and is very similar to Swing´s
GridBagLayout
or HTML´s table model. This approach makes this pane very well suited for any kind of form (like contact forms on a website).You have the ability to…
- apply any
Node
to a cell (specified by column and row) in theGridPane
- to let the
Node
span multiple columns/rows - to align the
Node
in the cell it was applied to - to set horizontal or vertical grow for the
Node
- and to apply a margin to be kept around the
Node
in the cell.
The flexibility of the GridPane
also extends to a very flexible API. You can use static class methods like setColumnIndex(node, index)
or setRowSpan(node, value)
, or you can use convenience instance methods like gridpane.add(node, column, row, columnSpan, rowSpan)
.
Note:
- You don´t have to set the maximum number of columns or rows in the
GridPane
as it will grow automatically. - The size of one column is automatically determined by the widest
Node
in this column, the height of each row is determined by the tallestNode
in the row.
The last note is probably the most important fact about the GridPane
as it has to be considered for the column/row and the column span/row span of every single Node
in order to get the layout you want.
For more complex layouts it is a very good idea to draw the layout on a piece of paper and to draw all lines for the columns and rows. This will ease development because you can diretly see in which cell you have to put each Node
and how many rows or columns they have to span.
Lets have a look at the first simple example:
GridPane – Example 1
import javafx.application.Application; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.stage.Stage; /** * Created on: 23.06.2012 * @author Sebastian Damm */ public class GridPaneExample extends Application { @Override public void start(Stage primaryStage) throws Exception { GridPane gridPane = new GridPane(); gridPane.setPadding(new Insets(40, 0, 0, 50)); gridPane.setHgap(5); gridPane.setVgap(5); Scene scene = new Scene(gridPane, 300, 150); Label lbUser = new Label('Username:'); GridPane.setHalignment(lbUser, HPos.RIGHT); TextField tfUser = new TextField(); Label lbPass = new Label('Password:'); GridPane.setHalignment(lbPass, HPos.RIGHT); PasswordField tfPass = new PasswordField(); Button btLogin = new Button('Login'); GridPane.setMargin(btLogin, new Insets(10, 0, 0, 0)); gridPane.add(lbUser, 0, 0); gridPane.add(tfUser, 1, 0); gridPane.add(lbPass, 0, 1); gridPane.add(tfPass, 1, 1); gridPane.add(btLogin, 1, 2); primaryStage.setTitle('GridPaneExample 1'); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { Application.launch(args); } }
Here you can see a little login form with with two labels and two textfields for the username and the password. Additionally theres a ‘login’ button.
In lines 21-23 we create the GridPane
and apply some padding. Furthermore you can specify a horizontal and a vertical gap to be kept between each Node
. Next, take a look at line 28: The alignment of a Node
inside the boundaries of the cell it was put into, can be set with the static class methods GridPane.setHalignment(Node node, HPos pos)
, respectively GridPane.setValignment(Node node, VPos pos)
.
In line 36 you can see how to put a individual margin around a single Node
by using the GridPane.setMargin(Node node, Insets insets)
method.
Finally in lines 38 to 42 we add each Node
to the GridPane
and specify the column and the row of the Node
.
Your application should look like this now:
In the next example you will see, why we need to set the column span and the row span of each Node
in more complex layouts. Have a look at this code:
GridPane – Example 2: User form
import javafx.application.Application; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.paint.RadialGradientBuilder; import javafx.scene.paint.Stop; import javafx.stage.Stage; /** * Created on: 23.06.2012 * @author Sebastian Damm */ public class GridPaneExample2 extends Application { private final Paint background = RadialGradientBuilder.create() .stops(new Stop(0d, Color.TURQUOISE), new Stop(1, Color.web('3A5998'))) .centerX(0.5d).centerY(0.5d).build(); private final String LABEL_STYLE = '-fx-text-fill: white; -fx-font-size: 14;' + '-fx-effect: dropshadow(one-pass-box, black, 5, 0, 1, 1);'; @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createGridPane(), 370, 250, background); primaryStage.setTitle('GridPaneExample 2 - User form'); primaryStage.setScene(scene); primaryStage.show(); } private GridPane createGridPane() { GridPane gridPane = new GridPane(); gridPane.setPadding(new Insets(20, 0, 20, 20)); gridPane.setHgap(7); gridPane.setVgap(7); Label lbFirstName = new Label('First Name:'); lbFirstName.setStyle(LABEL_STYLE); GridPane.setHalignment(lbFirstName, HPos.RIGHT); TextField tfFirstName = new TextField(); Label lbLastName = new Label('Last Name:'); lbLastName.setStyle(LABEL_STYLE); GridPane.setHalignment(lbLastName, HPos.RIGHT); TextField tfLastName = new TextField(); Label lbCity = new Label('City:'); lbCity.setStyle(LABEL_STYLE); GridPane.setHalignment(lbCity, HPos.RIGHT); TextField tfCity = new TextField(); Label lbStreetNr = new Label('Street/Nr.:'); lbStreetNr.setStyle(LABEL_STYLE); GridPane.setHalignment(lbStreetNr, HPos.RIGHT); TextField tfStreet = new TextField(); tfStreet.setPrefColumnCount(14); GridPane.setColumnSpan(tfStreet, 2); TextField tfNumber = new TextField(); tfNumber.setPrefColumnCount(3); Label lbNotes = new Label('Notes:'); lbNotes.setStyle(LABEL_STYLE); GridPane.setHalignment(lbNotes, HPos.RIGHT); TextArea taNotes = new TextArea(); taNotes.setPrefColumnCount(5); taNotes.setPrefRowCount(5); GridPane.setColumnSpan(taNotes, 3); GridPane.setRowSpan(taNotes, 2); ImageView imageView = new ImageView(new Image(getClass() .getResourceAsStream('person.png'), 0, 65, true, true)); GridPane.setHalignment(imageView, HPos.LEFT); GridPane.setColumnSpan(imageView, 2); GridPane.setRowSpan(imageView, 3); // gridPane.setGridLinesVisible(true); gridPane.add(lbFirstName, 0, 0); gridPane.add(tfFirstName, 1, 0); gridPane.add(imageView, 2, 0); gridPane.add(lbLastName, 0, 1); gridPane.add(tfLastName, 1, 1); gridPane.add(lbCity, 0, 2); gridPane.add(tfCity, 1, 2); gridPane.add(lbStreetNr, 0, 3); gridPane.add(tfStreet, 1, 3); gridPane.add(tfNumber, 3, 3); gridPane.add(lbNotes, 0, 4); gridPane.add(taNotes, 1, 4); return gridPane; } public static void main(String[] args) { Application.launch(args); } }
In this example we create a user form with different inputs and an image. To make the application appear a little nicer, i created a RadialGradient
for the background of the Scene
and applied a white font color and a little dropshadow to each label.
The application should look like this:
Compared to the previous example, the first difference occurs in line 64.
With GridPane.setColumnSpan(tfStreet, 2);
i tell this TextField
to occupy two columns. This is needed, because i want this textfield to be a little wider (see line 63) than the other textfields. Otherwise the second column would be as wide as this textfield and therefore stretch the smaller ones.
The TextArea
(starting at line 71) and the ImageView
(line 77) span across multiple columns and rows.
Next, take a look at line 83. If you remove the comment lines and start the application, it should look like this:
As you can see, this method makes all grid lines (including the horizontal and vertical gap between each Node
visible which can be a great help if your Nodes
arent aligned the way you want it.
I don´t know how many times i wished for a method like this during the time i learned Swing and the GridBagLayout
and i bet that i´m not the only one ;)
Finally, please remove all lines, where column span or row span are specified (lines 64, 74, 75, 80, 81). This will help you to understand the necessity of column span and row span.
You can see, that each Node
occupies one single cell and that the layout is pretty messed up because the width/height of each column/row depend on the widest/tallest child Node
.
GridPane – Example 3: The setConstraints method
The instance method add
‘only’ provides two versions, one with the Node
, the column and the row, and one with additional column span and row span. Other properties like the alignment or the grow have to be set with dedicated class methods like GridPane.setHalignment
like in the first two examples.
But theres another nice way: the GridPane.setConstraints(...)
method. At the moment (JavaFX 2.2) there are five overloaded versions of this method from setConstraints(Node child, int columnIndex, int rowIndex)
to setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan, HPos halignment, VPos valignment, Priority hgrow, Priority vgrow, Insets margin)
.
This is pretty similiar to Swing´s GridBagConstraints
but here you don´t have to create a dedicated object and reuse it for multiple graphical objects.
If you apply the constraints to every Node
like this, you can simply add the Nodes
to the GridPane´s
collections of children.
With this approach the code of the second example looks like this:
private GridPane createGrid() { GridPane gridPane = new GridPane(); gridPane.setPadding(new Insets(20, 0, 20, 20)); gridPane.setHgap(7); gridPane.setVgap(7); Label lbFirstName = new Label('First Name:'); lbFirstName.setStyle(LABEL_STYLE); GridPane.setConstraints(lbFirstName, 0, 0, 1, 1, HPos.RIGHT, VPos.CENTER); TextField tfFirstName = new TextField(); GridPane.setConstraints(tfFirstName, 1, 0); Label lbLastName = new Label('Last Name:'); lbLastName.setStyle(LABEL_STYLE); GridPane.setConstraints(lbLastName, 0, 1, 1, 1, HPos.RIGHT, VPos.CENTER); TextField tfLastName = new TextField(); GridPane.setConstraints(tfLastName, 1, 1); Label lbCity = new Label('City:'); lbCity.setStyle(LABEL_STYLE); GridPane.setConstraints(lbCity, 0, 2, 1, 1, HPos.RIGHT, VPos.CENTER); TextField tfCity = new TextField(); GridPane.setConstraints(tfCity, 1, 2); Label lbStreetNr = new Label('Street/Nr.:'); lbStreetNr.setStyle(LABEL_STYLE); GridPane.setConstraints(lbStreetNr, 0, 3, 1, 1, HPos.RIGHT, VPos.CENTER); TextField tfStreet = new TextField(); tfStreet.setPrefColumnCount(14); GridPane.setConstraints(tfStreet, 1, 3, 2, 1); TextField tfNumber = new TextField(); tfNumber.setPrefColumnCount(3); GridPane.setConstraints(tfNumber, 3, 3); Label lbNotes = new Label('Notes:'); lbNotes.setStyle(LABEL_STYLE); GridPane.setConstraints(lbNotes, 0, 4, 1, 1, HPos.RIGHT, VPos.CENTER); TextArea taNotes = new TextArea(); taNotes.setPrefColumnCount(5); taNotes.setPrefRowCount(5); GridPane.setConstraints(taNotes, 1, 4, 3, 2); ImageView imageView = new ImageView(new Image(getClass() .getResourceAsStream('person.png'), 0, 65, true, true)); GridPane.setConstraints(imageView, 2, 0, 3, 3, HPos.LEFT, VPos.CENTER); gridPane.getChildren().addAll(lbFirstName, tfFirstName, imageView , lbLastName, tfLastName, lbCity, tfCity, lbStreetNr, tfStreet , tfNumber, lbNotes, taNotes); return gridPane; }
You can see the usage of the overloaded setConstraints(...)
methods and how you can simply add the Nodes
to the GridPane
in lines 51-53.
I hope i could provide a good introduction to the GridPane
in JavaFX 2.0. Feel free to add comments and post questions.
Reference: JavaFX 2.0 Layout Panes – GridPane from our JCG partner Sebastian Damm at the Just my 2 cents about Java blog.
superb tutorial thanks a lot.