How to organize and layout your GUI components in JavaFX application.
This is the fourth article in the JavaFX series. In the previous article, I described how to use FXML and SceneBuilder to create your user interface. Now we'll cover layouts.
Layouts, in a nutshell, are containers for components. This is useful as you can then position this container as a whole no matter what components are inside. Moreover, each scene can only hold one component, so you need a layout as a root component for your scene, so you can fit all the components you need. Of course, one layout is usually not enough, but you can nest layouts. That means you can put one layout inside another.
In addition to that, layouts also organize and position your components inside. Based on the layout used, child components can be positioned for example:
- One after another horizontally
- One after another vertically
- One on top of each other as a stack
- In grid
There are many more options. What's important is that a layout automatically updates the position of its children when it is resized. This way, you can have a consistent layout, even if your user resizes the application window.
This is one of the most straightforward layouts available. It just puts all the items inside horizontally in a row, one after another, from left to right.
In FXML, you can use HBox like this:
<HBox> <Button>1</Button> <Button>2</Button> <Button>3</Button> <Button>4</Button> </HBox>
In Java you can use this:
HBox hbox = new HBox(); Button btn1 = new Button("1"); Button btn2 = new Button("2"); Button btn3 = new Button("3"); Button btn4 = new Button("4"); hbox.getChildren().addAll(btn1, btn2, btn3, btn4);
Our elements are now neatly laid out horizontally in a row, one after another:
However, it does not look very good as they are right after each other with no spacing. Fortunately, we can define the spacing between components using
spacing property of the HBox:
<HBox spacing="10"> ... </HBox>
Or in Java using
HBox hbox = new HBox(); hbox.setSpacing(10);
The elements are now spaced properly, however, there is still no padding between elements and the HBox itself. It can be useful to add padding to our HBox:
You can define each section of the padding separately - top, bottom, left and right.
<HBox> <padding> <Insets top="10" bottom="10" left="10" right="10"/> </padding> ... </HBox>
The same thing can be achieved in Java:
HBox hbox = new HBox(); hbox.setPadding(new Insets(10, 10, 10, 10));
VBox is very similar to HBox, but instead of displaying the components inside horizontally in a row, it displays them vertically in a column:
You can still set the spacing and padding in the same way as with HBox.
In code, VBox is used exactly in the same way as HBox, just the name is different:
<VBox spacing="10"> <padding> <Insets top="10" bottom="10" left="10" right="10"/> </padding> <Button>1</Button> <Button>2</Button> <Button>3</Button> <Button>4</Button> </VBox>
And in Java:
VBox vbox = new VBox(); vbox.setPadding(new Insets(10, 10, 10, 10)); vbox.setSpacing(10); Button btn1 = new Button("1"); Button btn2 = new Button("2"); Button btn3 = new Button("3"); Button btn4 = new Button("4"); vbox.getChildren().addAll(btn1, btn2, btn3, btn4);
This layout is useful for stacking its components one on top of each other. The order of insertion determines the order of the items. That means the first item is on the bottom, the next is on top of it and so on.
This can be useful, for example, for having an image and then having some text or button on top of it.
The following example shows StackPane in FXML:
<StackPane> <ImageView> <Image url="/image.jpg"/> </ImageView> <Button>Click Me!</Button> </StackPane>
The same example in Java:
StackPane stackPane = new StackPane(); Image image = new Image(getClass().getResourceAsStream("/image.jpg")); ImageView imageView = new ImageView(image); Button btn = new Button("Click Me!"); stackPane.getChildren().addAll(imageView, btn);
You can set alignment of the items in the stack to better adjust their positioning:
<StackPane alignment="BOTTOM_CENTER"> ... </StackPane>
Of course, you can do the same in Java:
StackPane stackPane = new StackPane(); stackPane.setAlignment(Pos.BOTTOM_CENTER);
If you want even more fine-grained control of the positioning, you can set margin for individual items in the stack:
<StackPane alignment="BOTTOM_CENTER"> <ImageView> <Image url="/image.jpg"/> </ImageView> <Button> <StackPane.margin> <Insets bottom="10"/> </StackPane.margin> Click Me! </Button> </StackPane>
Or in Java:
StackPane stackPane = new StackPane(); Button btn = new Button("Click Me!"); stackPane.getChildren().add(btn); StackPane.setMargin(btn, new Insets(0,0,10,0));
Flow Pane can work in two modes - either horizontal (the default) or vertical.
In horizontal mode, the items are displayed horizontally, one after another, similar to HBox. The difference is, once there is no more horizontal space available, it wraps to the next row under the first one and continues again. This way there can be many rows, not just one as with HBox.
The vertical mode is very similar, but (like VBox) it displays items vertically, from top to bottom. Once there is no more space, it adds another column and continues.
The following image illustrates these two modes:
Note that items don't necessarily have to have the same size as they do have in the image above.
Note how the position of the components is recalculated if you change the size of the container:
You can set the internal padding of this layout in the same way as for HBox and VBox. Spacing is a little bit different though. Instead of just one property for spacing, you need to have separate horizontal and vertical spacing as the items can be rendered in multiple rows/columns. For horizontal spacing use
hgap, for vertical spacing use
FlowPane flowPane = new FlowPane(); flowPane.setOrientation(Orientation.VERTICAL); flowPane.setVgap(10); flowPane.setHgap(10); flowPane.getChildren().addAll(...);
Example of FlowPane in FXML:
<FlowPane hgap="10" vgap="10" orientation="VERTICAL"> ... </FlowPane>
This layout is very similar to FlowPane. The way it displays its components is almost identical. You can still have either horizontal or vertical mode and define
One crucial difference is in the sizing of the cells. FlowPane assigns only space necessary for each component. TilePane, on the other hand, makes the size of all cells the same depending on the biggest item. This way, all the controls are nicely aligned in rows/columns.
In the image above, there are the same components in the same order. You can clearly see the difference.
- FlowPane puts controls directly one after another, no extra spacing
- TilePane puts controls into cells with the same size based on the biggest element
Constructing TilePane is no different from FlowPane except for the name.
<TilePane vgap="10" hgap="10" orientation="VERTICAL" >
The same in Java:
TilePane tilePane = new TilePane(); tilePane.setVgap(10); tilePane.setHgap(10); tilePane.setOrientation(Orientation.VERTICAL);
In this article, we learned how to use several of the basic layout components in JavaFX. Still, there are more layouts to choose from. We'll cover these in the next article.