Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

VBox and HBox

Window elements are not placed by hand at coordinates; they go into layout panes, or containers. A container works out where each node goes and how big it is, and recalculates everything when the window is resized. The simplest containers are VBox (a column) and HBox (a row).

VBox, a column

VBox puts nodes one below another. The number in the constructor is the gap between them in pixels:

// src/main/java/com/example/notes/App.java — part of start()
VBox root = new VBox(8);
root.getChildren().addAll(
        new Label("Buy milk"),
        new Label("Call Oleg"),
        new Label("Read a book"));
root.setPadding(new Insets(16));

The window shows three labels in a column in the top-left corner. getChildren() is the list of the container's child nodes, and setPadding sets the inner space from the window edges. Insets lives in the javafx.geometry package.

HBox, a row

HBox puts nodes in a row from left to right. The nodes can go straight into the constructor after the gap:

// src/main/java/com/example/notes/App.java — part of start()
HBox root = new HBox(8, new Button("Add"), new Button("Delete"));
root.setAlignment(Pos.CENTER_RIGHT);
root.setPadding(new Insets(16));

Two buttons stand side by side at the right edge of the window, halfway down. setAlignment aligns the content: Pos.TOP_LEFT (the default), Pos.CENTER, Pos.BOTTOM_RIGHT and others.

Stretching

By default a node in an HBox gets its "natural" size. To make an input field take all the free space, give it a grow priority:

// src/main/java/com/example/notes/App.java — part of start()
HBox.setHgrow(titleField, Priority.ALWAYS);

VBox.setVgrow does the same for a VBox.

The first notebook screen

Let's build a column of a header, a row with a field and a button, and a label at the bottom:

// src/main/java/com/example/notes/App.java
package com.example.notes;
 
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
 
public class App extends Application {
 
    @Override
    public void start(Stage stage) {
        Label header = new Label("My notes");
 
        TextField titleField = new TextField();
        titleField.setPromptText("Note title");
        Button addButton = new Button("Add");
 
        HBox form = new HBox(8, titleField, addButton);
        HBox.setHgrow(titleField, Priority.ALWAYS);
 
        VBox root = new VBox(12, header, form, new Label("No notes yet"));
        root.setPadding(new Insets(16));
 
        stage.setTitle("Notes");
        stage.setScene(new Scene(root, 640, 480));
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}

Run mvn javafx:run. At the top is "My notes", below it an input field and an "Add" button. The field stretches across the full width and grows with the window, and the button stays at the right edge. Below is "No notes yet". At startup the cursor is already in the field, so the "Note title" hint is hidden: the grey text appears when focus moves to the button, for example with the Tab key.

Common mistake

The same node cannot be added to a container twice. The code new VBox(label, label) fails with java.lang.IllegalArgumentException: Children: duplicate children added: parent = VBox@101b462. A node can sit in only one place of the scene graph: if you need two identical labels, create two Label objects.

Thanks for reading!

Donate to the site — we work hard for you.