3.05 Layouts

Einleitung

Bisher haben wir in allen Beispiel das BorderPane-Layout verwendet. Zur Erinnerung und um hier eine vollständige Auflistung aller verfügbaren Layouts zu haben, sehen wir uns dieses noch einmal kurz an. Anschließend gehen wir auf die Alternativen ein.

Bist Du mit dem BorderPane-Layout bereits gut vertraut, kannst Du dieses natürlich hier überspringen.

Verfügbare Layouts

BorderPane

Anwendung mit BorderPane-Layout
Anwendung mit BorderPane-Layout

Hier wird die Szene in die fünf Bereiche center, top, left, bottom, right eingeteilt. Viele reale Anwendungen verwenden dies als grundlegende Aufteilung, wobei in den verschiedenen Bereichen dann mehrere Komponenten abgelegt werden. Dazu kommen wir am Ende, wenn wir Layouts verschachteln.

Hier ein einfaches Beispiel, das zu der Abbildung gehört:

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;

public class HalloFXBorder extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel BorderPane");

        // Erzeugen der GUI-Komponenten
        final Button btnOben = new Button("Top");
        final Button btnLinks = new Button("Left");
        final Button btnUnten = new Button("Botton");
        final Button btnRechts = new Button("Right");
        final Button btnMitte = new Button("Center");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final BorderPane root = new BorderPane();

        // Komponenten hinzufuegen
        root.setTop(btnOben);
        root.setLeft(btnLinks);
        root.setBottom(btnUnten);
        root.setRight(btnRechts);
        root.setCenter(btnMitte);

        // Ausrichtung der Komponenten
        // Weitere koennen ergaenzt werden
        BorderPane.setAlignment(btnOben, Pos.CENTER);
        BorderPane.setAlignment(btnUnten, Pos.CENTER);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 200, 200);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

VBox

Eine Anwendung mit VBox
In einer VBox werden die Komponenten vertikal angeordnet.

Bei diesem Layout werden die Komponenten untereinander angeordnet. Das Hinzufügen funktioniert hier anders als beim BorderPane-Layoout. Mit der Methode getChildren() erhalten wir eine Liste aller Kindelemente, die bereits hinzugefügt wurden. Dieser Liste wiederum können wir mit add() ein einzelnes Element oder mit addAll() direkt mehrere Elemente hinzufügen.
Mit setPadding() können wir den Außenabstand der VBox festlegen. Das ist der Abstand, den andere Elemente zu dieser Box  haben. (Wer mit CSS vertraut ist, kennt so etwas!) Über setSpacing() können wir den Abstand der Elemente innerhalb der Box bestimmen.

Hier ist ein Beispiel, mit dem Du etwas experimentieren kannst:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;

public class HalloFXVBox extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel VBox");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Eins");
        final Button btnZwei = new Button("Zwei");
        final Button btnDrei = new Button("Drei");
        final Button btnVier = new Button("Vier");
        final Button btnFuenf = new Button("Fuenf");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final VBox root = new VBox();

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Abstand Kompoenten in der HBox
        root.setSpacing(10);

        // Einzelne Komponente hinzufügen:
        root.getChildren().add(btnEins);

        // Mehrere Komponenten hinzufügen:
        root.getChildren().addAll(btnZwei, btnDrei, btnVier, btnFuenf);

        // Ausrichtung der Komponenten
        root.setAlignment(Pos.TOP_CENTER);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 100, 200);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

HBox

Das HBox-Layout funktioniert im Grunde wie das VBox-Layout, nur werden hier die Komponenten horizontal angeordnet.

Eine Anwendung mit HBox.
In einer HBox liegen die Komponenten nebeneinander.

Auch dazu ein Beispiel:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;

public class HalloFXHBox extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel HBox");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Eins");
        final Button btnZwei = new Button("Zwei");
        final Button btnDrei = new Button("Drei");
        final Button btnVier = new Button("Vier");
        final Button btnFuenf = new Button("Fuenf");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final HBox root = new HBox();

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Abstand Kompoenten in der HBox
        root.setSpacing(10);

        // Einzelne Komponente hinzufügen:
        root.getChildren().add(btnEins);

        // Mehrere Komponenten hinzufügen:
        root.getChildren().addAll(btnZwei, btnDrei, btnVier, btnFuenf);

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Abstand Kompoenten in der HBox
        root.setSpacing(10);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 400, 50);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

FlowPane

In diesem Layout werden die Komponenten nacheinander angeordnet, wobei wir uns aussuchen können, ob sie zeilenweise hinzugefügt werden und bei Zeilenende einfach in die nächste Zeile gewechselt wird oder ob sie untereinander angeordnet werden und bei Bedarf eine neue Spalte begonnen wird.

Dieses Layout reagiert flexibel auf eine Änderung der Fenstergröße wie man in der Abbildung sehen kann. In diesem Beispiel werden die Komponenten zeilenweise hinzugefügt.

Eine Anwendung mit FlowPane-Layout.
Dieselbe Anwendung mit zwei verschiedenen Fenstergrößen.

Dies ist der zugehörige Quelltext:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;

public class HalloFXFlow extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel FlowPane");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Eins");
        final Button btnZwei = new Button("Zwei");
        final Button btnDrei = new Button("Drei");
        final Button btnVier = new Button("Vier");
        final Button btnFuenf = new Button("Fuenf");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final FlowPane root = new FlowPane();

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Einzelne Komponente hinzufügen:
        root.getChildren().add(btnEins);

        // Mehrere Komponenten hinzufügen:
        root.getChildren().addAll(btnZwei, btnDrei, btnVier, btnFuenf);

        // Ausrichtung der Komponenten
        root.setAlignment(Pos.TOP_LEFT);

        // Orientierung
        root.setOrientation(Orientation.HORIZONTAL);

        // Abstaende zwischen den Komponenten
        root.setHgap(30);
        root.setVgap(20);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 200, 200);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

GridPane

Anwendung mit GridPane
Hier wurde das Gitter sichtbar gemacht.

Bei diesem Layout können wir eine Anzahl von Zeilen und Spalten und somit ein Gitter festlegen und in diesen dann unsere Komponenten verteilen.
Über SetPadding() können wir auch hier den Außenabstand bestimmen. Außerdem können wir mit setHgap() den Zeilenabstand und mit setVgap() den Spaltenabstand festlegen.
Komponenten werden einzeln mit add() hinzugefügt. Dabei müssen wir aber nicht nur die Komponente, sondern auch die Spalte und die Zeile angeben, in der sie erscheinen soll. Zusätzlich können wir optional angeben, über wie viele Spalten oder Zeilen sich die Komponente erstrecken soll – das geschieht im Beispiel beim vierten Button.

Die add-Methode sieht formal also so aus:
add(Komponente, Spaltennummer, Zeilennummer)
add(Komponente, Spaltennummer, Zeilennummer, Anzahl Spalten, Anzahl Zeilen)

Zum Üben ist es sehr empfehlenswert mit setGridLinesVisible() das Gitter sichtbar zu machen.

Hier unser Beispiel:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;

public class HalloFXGrid extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel GridPane");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Eins");
        final Button btnZwei = new Button("Zwei");
        final Button btnDrei = new Button("Drei");
        final Button btnVier = new Button("Ein breiter Button");
        final Button btnFuenf = new Button("Fuenf");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final GridPane root = new GridPane();

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Abstände der Elemente
        root.setVgap(30);
        root.setHgap(20);

        // Komponenten hinzufügen:
        root.add(btnEins, 0, 0);
        root.add(btnZwei, 0, 1);
        root.add(btnDrei, 1, 1);
        root.add(btnVier, 0, 2, 2, 1);
        root.add(btnFuenf, 1, 3);

        // Zur Übung:
        root.setGridLinesVisible(true);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 200, 240);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

TilePane

Das TilePane-Layout ist dem FlowPane-Layout sehr ähnlich. Die Komponenten werden hier ebenso einfach nacheinander hinzugefügt und dabei horizontal oder vertikal angeordnet. Der Unterschied liegt darin, dass dabei automatisch ein Gitter erstellt wird, in dem alle Zellen („Tiles“) die gleiche Größe haben. Dabei wird sich zwangsweise am breitesten bzw. höchsten Element orientiert.

Eine Anwendung mit TilePane-Layout.
Der breite Button sorgt dafür, dass alle Spalten sehr breit sind.

Dieses Layout wirkt dadurch aufgeräumter als das FlowPane-Layout. Gleichzeitig kann dadurch aber auch mehr Platz ungenutzt bleiben.
Sehen wir uns zum Vergleich dieselbe Anwendung wie in der Abbildung oben noch einmal mit FlowPane-Layout an:

Hier nimmt jedes Element so viel Platz ein wie es braucht.

AnchorPane

Bei diesem Layout können wir die Komponenten an frei wählbaren Positionen verankern. Genauer gesagt können wir angeben, wie weit eine Komponente von den Rändern des Layouts entfernt sein soll. Dabei können wir uns aussuchen, an welchen Rändern – oben, unten, links oder rechts – sich eine Komponente orientieren soll.

Sehen wir uns ein Beispiel an:

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public class HalloFXAnchor extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel AnchorPane");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Eins");
        final Button btnZwei = new Button("Zwei");
        final Button btnDrei = new Button("Drei");
        final Button btnVier = new Button("Vier");
        final Button btnFuenf = new Button("Fuenf");

        // Erzeugen des Layouts und Platzieren der Komponenten
        final AnchorPane root = new AnchorPane();

        // Festlegen der Ankerpunkte
        AnchorPane.setLeftAnchor(btnEins, 10.0);
        AnchorPane.setTopAnchor(btnEins, 10.0);

        AnchorPane.setLeftAnchor(btnZwei, 50.0);
        AnchorPane.setTopAnchor(btnZwei, 50.0);

        AnchorPane.setLeftAnchor(btnDrei, 90.0);
        AnchorPane.setTopAnchor(btnDrei, 90.0);

        AnchorPane.setLeftAnchor(btnVier, 130.0);
        AnchorPane.setTopAnchor(btnVier, 130.0);

        AnchorPane.setLeftAnchor(btnFuenf, 150.0);
        AnchorPane.setBottomAnchor(btnFuenf, 10.0);

        // Einzelne Komponente hinzufügen:
        root.getChildren().add(btnEins);

        // Mehrere Komponenten hinzufügen:
        root.getChildren().addAll(btnZwei, btnDrei, btnVier, btnFuenf);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 240, 240);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}
Der 5. Button ist am unteren Rand verankert.

Hier orientieren sich die ersten vier Buttons am oberen und am linken Rand. Der fünfte Button hingegen orientiert sich am linken und am unteren Rand. Verschieben wir den unteren Rand, wandert dieser Button mit.

StackPane

Anwendung mit StackPane-Layout
Zwei Buttons wurden aufeinander gestapelt.

Dieses Layout fällt etwas aus der Reihe. Hier werden die Komponenten an ein- und derselben Stelle gestapelt. Mit den Komponententypen, die wir bisher kennen, ergibt dies wenig Sinn. Interessanter wird dies, wenn wir beispielsweise mit einfachen Grafikelementen etwas zeichnen wollen.

Der Vollständigkeit halber sehen wir uns aber trotzdem schon jetzt ein Beispiel für dieses Layout an:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;

public class HalloFXStack extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel StackPane");

        // Erzeugen der GUI-Komponenten
        final Button btnEins = new Button("Button Nummer Eins");
        final Button btnZwei = new Button("Zwei");

        
        // Erzeugen des Layouts und Platzieren der Komponenten
        final StackPane root = new StackPane();

        // Aussenabstand, z.B. zum Fensterrand
        root.setPadding(new Insets(10, 20, 10, 20));

        // Mehrere Komponenten hinzufügen:
        root.getChildren().addAll(btnEins, btnZwei);

        // Ausrichtung der Komponenten
        root.setAlignment(Pos.TOP_LEFT);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 200, 50);
        meineStage.setScene(myScene);
        meineStage.show();
 
    }

}

Verschachteln von Layouts

Wie oben schon einmal erwähnt, werden die Layouts wesentlich spannender und flexibler, wenn wir beginnen, sie zu verschachteln.

Eine Anwendung, die an einen einfachen Texteditor erinnert.
Verschachtelte Layouts geben uns viele Gestaltungsmöglichkeiten.

In diesem Beispiel wird das BorderPane-Layout als umgebenes Layout verwendet. In seinem top– und bottom-Bereich werden HBox-Layouts abgelegt und analog dazu im left– und right-Bereicht VBox-Layouts.
In der Mitte wurde lediglich eine Text-Area ergänzt.
Auf diese Weise erhalten wir eine Einteilung des Fensters, die bereist an eine typische Software zur Textverarbeitung erinnert.

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.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class HalloFXVerschachtelt extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage meineStage) throws Exception {

        // Titel
        meineStage.setTitle("Beispiel FX");

        // Erzeugen der GUI-Komponenten
        final Button neu = new Button("Neu");
        final Button oeffnen = new Button("Öffnen");
        final Button speichern = new Button("Speichern");

        final Button aktion1 = new Button("Eins");
        final Button aktion2 = new Button("Zwei");
        final Button aktion3 = new Button("Drei");

        final Button aktionA = new Button("A");
        final Button aktionB = new Button("B");
        final Button aktionC = new Button("B");

        final Label labelX = new Label("X");
        final Label labelY = new Label("Y");
        final Label labelZ = new Label("Z");

        final TextArea area = new TextArea();

        // Erzeugen des Layouts und Platzieren der Komponenten
        final BorderPane root = new BorderPane();
        final HBox topBox = new HBox();
        final VBox leftBox = new VBox();
        final HBox bottomBox = new HBox();
        final VBox rightBox = new VBox();

        topBox.setPadding(new Insets(10, 20, 10, 20));
        topBox.setSpacing(10);

        leftBox.setPadding(new Insets(10, 20, 10, 20));
        leftBox.setSpacing(20);

        rightBox.setPadding(new Insets(10, 20, 10, 20));
        rightBox.setSpacing(20);

        bottomBox.setPadding(new Insets(10, 20, 10, 20));
        bottomBox.setSpacing(40);

        topBox.getChildren().addAll(neu, oeffnen, speichern);
        leftBox.getChildren().addAll(aktion1, aktion2, aktion3);
        bottomBox.getChildren().addAll(aktionA, aktionB, aktionC);
        rightBox.getChildren().addAll(labelX, labelY, labelZ);

        root.setTop(topBox);
        root.setLeft(leftBox);
        root.setCenter(area);
        root.setBottom(bottomBox);
        root.setRight(rightBox);

        // Festlegen der Szene
        final Scene myScene = new Scene(root, 400, 400);
        meineStage.setScene(myScene);
        meineStage.show();

    }

}

Wir könnten die Layouts nun auch noch weiter verschachteln. Das Prinzip ist dabei aber wieder dasselbe wie hier, so dass uns dieses Beispiel genügen sollte.