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
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
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.
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.
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
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.
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:
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(); } }
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
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.
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.