Wie verwendet man ein GridPane?

Ein GridPane ist geeignet, hinzugefügte Nodes rasterartig anzuordnen. Dabei müssen Spalten und Zeilen keine identische Größe besitzen und können unterschiedlich konfiguriert werden.

Der Ausgangszustand

Als Ausgangsbasis erzeugen wir eine einfache recht sinnfreie Anwendung mit einem Textbereich, einem Label und einem Button. Das gesamte Fenster besteht aus zwei Zeilen und zwei Spalten zunächst ohne weitere Konfiguration, von denen je zwei neben- und übereinander angeordnet sind. Der Textbereich befindet sich links oben, das Label links unten und der Button rechts unten. Die genannten Kindknoten werden hier durch die Methode add() unter Angabe der Spalten- und Zeilenindices direkt dem GridPane hinzugefügt.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class GridLayoutBsp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Label label = new Label("Label");
        Text text = new Text(0,0,"Ene mene Maus und Du bist raus!");
        Button button = new Button("klick");
        GridPane grid = new GridPane();

        grid.add(text, 0, 0);
        grid.add(label, 0, 1);
        grid.add(button, 1, 1);
        
        Scene scene = new Scene(grid);
        primaryStage.setScene(scene);
        primaryStage.setTitle("GridPane Beispiel");
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch();
    }
}

Das Hinzufügen der Knoten zum GridPane kann auf verschiedene Weisen erfolgen. So wäre statt der oben angeführten Zeile zum Setzen des Label-Objektes auch folgende Version möglich, bei der nach Setzen der Spalten- und Zeilenidices das Label der ObservableList aller Knoten des GridPane hinzugefügt wird:

GridPane.setRowIndex(label, 1);
GridPane.setColumnIndex(label, 0);
grid.getChildren().add(label);

Bei der Erscheinung des erzeugten Anwendungsfensters fällt zunächst auf, dass alle Elemente so gerade in ihre Zellen passen, deren Größe sich also nach der bevorzugten Größe der GUI-Elemente richtet und der Inhalt jeweils linksbündig ausgerichtet ist. Der Textbereich mit dem Abzählreim besetzt tatsächlich nur die Zelle links oben, expandiert diese jedoch nach Platzbedarf.

Ausgangszustand mit GridPane

Wird der Text durch \n folgendermaßen umgebrochen,

Text text = new Text("Ene mene Maus\nund Du bist raus!");

so ändert sich die Textanordnung im Text-Knoten und mit diesem auch die Größe des gesamten Fensters, ohne dass zusätzliche Angaben nötig wären.

Ausgangszustand mit GridPane nach Zeilenumbruch

Man kann dies gut verdeutlichen, indem man durch

grid.setGridLinesVisible(true);

die Anzeige der Rasterlinien aktiviert. Es ist zu erkennen, dass sich Breite und Höhe der Zellen nach dem Platzbedarf des in der jeweiligen Spalte und Zeile befindlichen GUI-Elementes richtet. Hieraus resultieren unterschiedliche Zellengrößen - ein wesentlicher Unterschied etwa zum GridLayout in Swing.

Ausgangszustand mit GridPane nach Vergrößern

Vergrößert man das Fenster durch Ziehen mit der Maus, so bleibt das gesamte GridPane links oben verankert und seine Elemente wachsen in der Standardeinstellung nicht mit.

Ausgangszustand mit GridPane und Rasterlinien nach Vergrößern

Ein Verschmelzen von Zellen kann durch die statische Methoden GridPane#setColumnSpan() bzw. GridPane#setRowSpan() erreicht werden. Hierzu muss der zu erweiternde Knoten, sowie die Anzahl der Zellen, die verbunden werden sollen als Parameter übergeben werden.
Das Ausführen von

GridPane.setColumnSpan(text, 2);

im Beispiel erzeugt nach leichtem Vergrößern des Fensters mit der Maus die folgende Erscheinung

Überspannen von zwei Spalten

Aus den Linienverläufen des Rasters kann man entnehmen, dass beim Überspannen kein wirkliches Verschmelzen der Rasterzellen stattfindet, sondern lediglich eine Erweiterung des Zelleninhalts erfolgt.

Erweitert man dies insofern, dass zusätzlich noch zwei Zeilen überspannt werden durch

GridPane.setRowSpan(text, 2);

so ergibt sich das folgende hier unbrauchbare Fenster.

Überspannen von je zwei Reihen und Spalten

Die Schichtungsreihenfolge resultiert hierbei aus der Reihenfolge des Hinzufügens der GUI-Knoten zum GridPane.

Positionierung von Zelleninhalten

Die beiden statischen Methoden GridPane#setHalignment() und GridPane#setValignment() steuern die Ausrichtung der Kindelemente im Raster. Eine Angabe von

GridPane.setHalignment(label, HPos.RIGHT);

führt bezüglich des Labels zu folgendem Aussehen

Rechtsbündigkeit des Labels

Als erster Parameter wird der betroffene Kindknoten angegeben, als zweiter Parameter stehen hier die folgenden selbsterklärenden Angaben zur Verfügung:

Für die vertikale Ausrichtung durch setValignment() stehen analog die folgenden Enum-Konstanten zur Verfügung:

Die Vergrößerung oder Verkleinerung von Zellen in vertikale und horizontale Richtung beim Aufziehen des Fensters kann durch die statischen Methoden GridPane#setVgrow() und GridPane#setHgrow() gesteuert werden. Der in Ergänzung zum Knoten als zweiter zu übergebene Parameter ist hier vom Enum-Typ Priority. Sein Einfluss wird in der folgenden Tabelle dargestellt.

GridPane.setHgrow(label, Priority.ALWAYS);
GridPane.setHgrow(button, Priority.ALWAYS);
Priority.ALWAYS/ALWAYS
GridPane.setHgrow(label, Priority.ALWAYS);
GridPane.setHgrow(button, Priority.SOMETIMES);
Priority.ALWAYS/SOMETIMES
GridPane.setHgrow(label, Priority.SOMETIMES);
GridPane.setHgrow(button, Priority.SOMETIMES);
Priority.SOMETIMES/SOMETIMES
GridPane.setHgrow(label, Priority.NEVER);
GridPane.setHgrow(button, Priority.NEVER);
Priority.NEVER/NEVER

Beim ersten und dritten Beispiel ist zu erkennen, dass die Zellen mit Label und mit Button beim Aufziehen des Fensters in gleichem Maße wachsen. Man beachte, dass das Label-Feld im Ausgangszustand, bedingt durch die darüber angeordnete Text-Komponente, bereits einen größeren Platz beansprucht. Der Zuwachs ist in beiden Zellen identisch.

Zellen-, Zeilen- und Spaltenformatierung durch Constraints

Möchte man die Formatierung von Zellen gezielt beeinflussen, so kann dies für den einzelnen Kindknoten oder zeilen- bzw. spaltenweise erfolgen.

Zeilen- und spaltenweise Zuweisung von Constraints

Die Klassen RowConstraints und ColumnConstraints stellen Werkzeuge zur zeilen- und spaltenweisen Formatierung zur Verfügung. Ihre Konstruktoren sind jeweils vierfach überladen und nehmen bei der Bildung entweder keinen, einen, drei oder sechs Parameter entgegen. Am Beispiel von RowConstraints soll der Umgang hiermit beispielhaft demonstriert werden. Die Deklaration von ColumnConstraints erfolgt analog.

RowConstraints rc = new RowConstraints(
                100,             // minimale Höhe
                100,             // bevorzugte Höhe
                100,             // maximale Höhe
                Priority.ALWAYS, // vertikale Ausdehnung
                VPos.BOTTOM,     // vertikale Anordnung
                true);           // vertikale Füllung

Constraints können auf unterschiedliche Art und Weise an ein GridPane übergeben werden. Eine Methode besteht darin, sich die ObservableList aller RowConstraints des GridPane mit getRowConstraints() zu holen und dieser die deklarierten Constraints hinzuzufügen.

grid.getRowConstraints().add(rc);

Die o.a. Constraints führen zur folgenden Ausgabe.

Anwendung von RowConstraints

Wird nur eine RowConstraints-Angabe gemacht, so bezieht sich diese auf die erste Zeile von oben. Für jede Zeile können gesonderte Constraints angegeben werden, die mit Hilfe der Methode addAll() gemeinsam dem GridPane hinzugefügt werden können. Die Methode addAll() kann eine variable Parameterzahl entgegennehmen, die von oben nach unten die einzelnen Zeilen formatieren.

RowConstraints rc1 =
    new RowConstraints(100, 100, 100, Priority.ALWAYS, VPos.BOTTOM, true);
RowConstraints rc2 =
    new RowConstraints(50, 50, 50, Priority.SOMETIMES, VPos.CENTER, true);
grid.getRowConstraints().addAll(rc1, rc2);

Eine wahlfreie Ansprache einzelner Zeilen ist hierbei jedoch nicht möglich. Soll eine Zeile nicht von der Standarddarstellung abweichend formatiert werden, so muss bei diesem Vorgehen ein leeres RowConstraints-Objekt übergeben werden.

Constraints für einzelne Knoten

Eine zweite auch hinsichtlich der Formatierungsmöglichkeiten etwas abweichende Vorgehensweise bei der Übergabe von Constraints an ein GridPane besteht durch die fünffach überladene statische Methode GridPane#setConstraints(). Hiermit ist auch die wahlfreie Ansprache einzelner Kindknoten möglich. Einen Überblick über die Methodenversionen gibt die folgende Auflistung:

setConstraints(Node child, int columnIndex, int rowIndex)
setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan)
setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan, HPos halignment, VPos valignment)
setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan, HPos halignment, VPos valignment, Priority hgrow, Priority vgrow)
setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan, HPos halignment, VPos valignment, Priority hgrow, Priority vgrow, Insets margin)

Ein Aufruf der letzten Methodenversion mit zehn Parametern

GridPane.setConstraints(
    text,            // Knotenelement
    0,               // Spaltenindex
    0,               // Zeilenindex
    2,               // Anzahl überspannter Spalten
    1,               // Anuahl überspannter Zeilen
    HPos.CENTER,     // horizonale Anordnung
    VPos.BOTTOM,     // vertikale Anordnung
    Priority.ALWAYS, // horizontale Ausdehnung
    Priority.ALWAYS, // vertikale Ausdehnung
    new Insets(20)   // Abstand des Inhaltes zum Zellenrand
);

führt nach Vergrößerung des Fensters zu der folgenden Ansicht

Zuweisung von Constraints zum GridPane

Es ist erkennbar, dass auf diese Weise die Ansprache eines ausgewählten Knotens, hier des Text-Objektes, und damit einer bestimmten Zelle eines GridPane möglich ist.

Allerdings haben die Formatierungen einzelner Zellen wiederum Auswirkungen auf diejenigen anderer Zellen. Hier ist auffällig, dass trotz der Angabe von Priority.ALWAYS für die horizontale Ausdehnung der Text-Zelle diese auf die Ausgangsbreite fixiert ist und nicht horizontal expandiert werden kann. Die Ursache ist in der unteren Zeile zu suchen, deren Elemente keine Ausdehnung zulassen. Erlaubt man die Ausdehnung des Label-, des Button-Objektes oder beider z.B. durch Vergabe von Priority.ALWAYS, so ist auch eine horizontale Expansion möglich.

Die zur obigen Angabe zusätzliche Ausdehnung des Button-Objektes durch

GridPane.setHgrow(button, Priority.SOMETIMES);

führt zur folgenden Ausgabe:

Zusätzliches Expandieren des Button-Objektes

Diejenige des Label-Objektes zu dieser:

Zusätzliches Expandieren des Label-Objektes

Hier zeigt sich im Vergleich, dass zusätzlich zum Expansionsverhalten auch die Position des Abzählreims im Text-Objekt durch diese Angabe beeinflusst wird.
Ausführliches Testen eines gewünschten Layouts ist somit in jedem Falle angeraten.

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.