13.02.03 Die Layoutmanager

Im letzten Kapitel haben Sie bereits einen Button auf ein Fenster gesetzt. Allerdings hat er sich Ihren Angaben bezüglich der Position und der Größe widersetzt. Der Grund dafür sind die Layoutmanager von Java. In diesem Kapitel werden Sie diese kennenlernen und verwenden.

Was ist ein Layoutmanager?

Vor den Layoutmanagern setzte man die Größe und Position einer Komponente mithilfe von Pixelwerten. Dies hat aber den Nachteil, dass man viel Geduld mitbringen muss, bis alle Komponenten richtig sitzen und groß genug sind. Außerdem muss man sich darüber Gedanken machen, was passiert, wenn das Fenster vom Benutzer skaliert wird. Entweder man verbietet dies von vornherein, man lässt die Komponenten einfach an ihrem Platz und nimmt in Kauf, dass die Komponenten vereinsamt in einer Fensterecke hängen oder gar abgeschnitten werden oder man entwickelt einen Algorithmus, der die Komponenten automatisch neu anordnet.
Letzteres übernehmen die Layoutmanager. Jeder Layoutmanager verfolgt eigene Regeln zum Anordnen von Komponenten, Sie können sich den für Sie passenden heraussuchen.
Fast alle Layoutmanager liegen im Paket java.awt, nur das BoxLayout ist im Paket javax.swing enthalten.
Jedem Container (z. B.: JFrame, JPanel) lassen sich Layoutmanager zuweisen.

Ein JPanel ist eine meist unsichtbare Komponente, die in erster Linie als Container für andere Komponeten dient. Es wird über den Konstruktor javax.swing.JPanel() instanziert. Ein Panel hat die selben Funktionen wie ein Frame, mit dem Unterschied, dass ein Panel immer in einem Frame oder einem anderen Panel liegt.

Das BorderLayout

Das BorderLayout teilt den Container in 5 Zonen ein:

Der Code zu obigen Beispiel sieht wie folgt aus. Die Klasse Borderlayout liegt im Paket java.awt. Die Klassen JButton und JFrame im Paket javax.swing.

JFrame frame = new JFrame("BoderLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton norden = new JButton("NORTH");
JButton sueden = new JButton("SOUTH");
JButton westen = new JButton("WEST");
JButton osten = new JButton("EAST");
JButton center = new JButton("CENTER");
frame.add(norden, BorderLayout.NORTH);
frame.add(sueden, BorderLayout.SOUTH);
frame.add(westen, BorderLayout.WEST);
frame.add(osten, BorderLayout.EAST);
frame.add(center, BorderLayout.CENTER);
frame.setVisible(true);

Wir müssen also jeder einzelnen Komponente sagen, in welcher Zone sie platziert werden soll. Wird eine Zone doppelt belegt, so ist nur die zuletzt gesetzte Komponente sichtbar. Der LayoutManager regelt jetzt automatisch auch das Verteilen des vorhandenen Platzes sobald der Container neu skaliert wird. Dabei geht das BorderLayout wie folgt vor:
Die Zonen NORTH und SOUTH bekommen die sämtliche verfügbare Breite und ihre bevorzugte Höhe. Die Zonen WEST und EAST bekommen die noch verfügbare Höhe, aber ihre bevorzugte Breite. Die Zone CENTER bekommt den Rest. Normalerweise hängt die Breite vom Inhalt der Komponente (z.B. Text) ab, die Höhe in der Regel vom verwendeten Look-And-Feel. Wenn Sie die Werte trotzdem anders setzen wollen, können Sie so festlegen:

component.setPreferredSize(new Dimension(int x, int y)); // Dimension liegt im Paket java.awt.

Das BorderLayout ist das Standardlayout für Frames. Aus diesem Grund muss es einem Frame nicht explizit zugewiesen werden. Sollte es dennoch mal vonnöten sein, können Sie das BorderLayout so zuweisen:

container.setLayout(new BorderLayout());

Im Konstruktor können Sie zusäzlich nocht die beiden Parameter Hgap und Vgap übergeben. Diese bestimmen dann den horizontalen bzw. den vertikalen Abstand der Zonen:

BorderLayout layout = new BorderLayout(40,10);

Obiges Beispiel legt fest, dass der horizontale Abstand 40 Pixel beträgt und der vertikale 10. Diese Werte lassen sich im Nachhinein durch die Methoden setHgap(int hgap) und setVgap(int vgap) ändern.

Wenn Sie die Angabe über die Zone weglassen, dann wird die Komponente automatisch in die Mitte gesetzt (vgl. 13.02.02 Der JButton). Es müssen nicht immer alle Zonen belegt werden, nicht belegte Zonen bleiben leer und der der Platz wird, wie oben angegeben, verteilt.

Das FlowLayout

Das FlowLayout ordnet die Komponenten nebeneinander an. Sobald die Komponenten nicht mehr in eine Zeile passen, wird umgebrochen und die betreffende Komponente landet in der nächsten Zeile, ähnlich wie in einem Browser. Alle Komponenten bekommen ihre bevorzugte Höhe und Breite. Auch das FlowLayout lässt sich jedem Container zuweisen, beim JPanel ist es das Standardlayout. Wollen Sie es beispielsweise in einem JFrame verwenden, müssen Sie dies festlegen:

JFrame frame = new JFrame("FlowLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("kurzer Text");
JButton button2 = new JButton("mit gesetzter Größe");
button2.setPreferredSize(new Dimension(50,46)); // eine bevorzugte Größe setzen
JButton button3 = new JButton("dies ist ein sehr langer Text ohne gesetzte Größe");
frame.setLayout(new FlowLayout()); //das FlowLayout muss explizit gesetzt werden
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.setVisible(true);

Die Ausrichtung der Komponenten kann mit den Konstanten FlowLayout.LEFT, FlowLayout.CENTER und FlowLayout.RIGHT festgelegt werden. Standardmäßig werden die Komponenten in die Mitte gesetzt. Diese Konstanten werden mit der Methode setAlignment(int align) verwendet. Alternativ können Sie die Ausrichung auch gleich im Konstruktor angeben:

FlowLayout flowLayout = new FlowLayout(FlowLayout.RIGHT);

Zudem können Sie wie auch schon beim BorderLayout die Abstände zwischen den Komponenten setzen. Dazu verwenden Sie entweder setHgap(int hgap) bzw. setVgap(int vgap) oder Sie geben die Abstände im Konstrukor hinter der Ausrichtung an:

FlowLayout flowLayout = new FlowLayout(FlowLayout.RIGHT, 10, 20);

Im Normalfall beträgt der Abstand 5 Pixel.

Das GridLayout

Das Gridlayout ordnet seine Komponenten in einer Tabelle an. Dabei lässt sich die Anzahl der Spalten bzw. die Anzahl der Zeilen bestimment. Jede Komponente bekommt exakt so viel Platz wie vorhanden ist. Die bevorzugte Größe spielt dabei überhaupt keine Rolle.
Ein GridLayout wird über den Konstruktor GridLayout(int rows, int cols) erzeugt.
Die Anzahl der Zeilen hat eine schwerere Gewichtung als die Anzahl der Spalten. Wenn Sie zum Beispiel ein GridLayout mit den Maßen 1×2 angelegt haben, aber drei Komponenten hinzufügen, wird eine dritte Spalte erzeugt, statt eine neue Zeile angefangen.
Sie können aber auch eine der beiden Angaben auf 0 setzen. Dann wir nur noch die andere Angabe beachtet. 0 Zeilen, 3 Spalten bedeutet dann, dass auf jeden Fall 3 Spalten gefüllt werden, bis eine neue Zeile beginnt. Andersherum, also wenn die die Anzahl der Spalten 0 ist, dann wird auf die Anzahl der Zeilen geachtet. Sind beide Parameter gleich 0, dann wird eine IllegalArgumentException geworfen.
In folgendem Code wird die Anzahl der Spalten bestimmt, die Anzahl der Zeilen richtet sich nach der Anzahl der Komponenten:

JFrame frame = new JFrame("GridLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("kurzer Text");
JButton button2 = new JButton("mit gesetzter Größe");
JButton button3 = new JButton("dies ist ein sehr langer Text ohne gesetzte Größe");
frame.setLayout(new GridLayout(0, 2));
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.setVisible(true);

Über den erweiterten Konstruktor Gridlayout(int rows, int cols, int hgap, int vgap) lässt sich auch noch der horizontale und vertikale Abstand zwischen den Komponenten festlegen. Wenn Sie den Standardkonstruktor verwenden bekommen Sie ein GridLayout mit 1 Zeile und einer Spalte.

Das BoxLayout

Das BoxLayout ordnet Ihre Komponenten entweder übereinander oder nebeneinander an. Das BoxLayout verfügt nur über einen Konstruktor: BoxLayout(Container target, int axis).
target gibt die Komponente an, auf die das Layout angewendet werden soll. Im Regelfall ist dies ein Panel. Wenn es ein Frame oder Dialog ist, dann müssen Sie als target das Ergebnis der Methode getContentPane() des Frames bzw. des Dialoges übergeben.
Der zweite Parameter des Konstruktors, axis, beschreibt die Art, wie die Komponenten angeordnet werden. Im Wesentlichen gibt es BoxLayout.X_AXIS, welches für die horizontale Anordnung sorgt und BoxLayout.Y_AXIS, dies sorgt für die vertikale Anordnung.
Beim BoxLayout bekommen alle Komponenten ihre bevorzugte Größe.
Folgendes Beispiel ordnet drei Buttons übereinander an Beachten Sie, dass das BoxLayout, anders als die anderen Layoutmanager, im Paket javax.swing liegt.

JFrame frame = new JFrame("BoxLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("kurzer Text");
JButton button2 = new JButton("mit gesetzter Größe");
JButton button3 = new JButton("dies ist ein sehr langer Text ohne gesetzte Größe");
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.setVisible(true);

Das Null-Layout

Das Null-Layout ist kein Layout in dem Sinne einer eigenen Klasse, sondern wird erreicht, wenn Sie einem Container mittels der setLayout(LayoutManager manager)-Funktion null zuweisen. In diesem Falle müssen Sie sich aber um die Größe und Positionierung jeder einzelnen Komponente kümmern, was bei größeren Anwendungen schon mal aufwändig werden kann. Außerdem wird nicht geregelt, was passiert, wenn das Fenster skaliert wird. In der Regel ist der Einsatz eines Layoutmanagers immer weniger arbeitsintensiv.
Untenstehender Code setzt mit Hilfe des null-Layouts drei Buttons auf einen Frame:

JFrame frame = new JFrame("Null Layout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("kurzer Text");
button1.setBounds(4, 10, 20, 70);
JButton button2 = new JButton("längerer");
button2.setBounds(20, 30, 40, 40);
JButton button3 = new JButton("dies ist ein sehr langer Text auf einem Button");
button3.setBounds(40, 80, 80, 5);
frame.setLayout(null);
frame.add(button1);
frame.add(button2);
frame.add(button3);
frame.setVisible(true);

Die Kombination macht’s

Es gibt nahezu keine Situation, in der Sie mit nur einem hier vorgestelltem Layoutmanager auskommen, Sie müssen fast immer kombinieren, indem Sie Panels in Panels, aber mit anderen Layoutmangern setzen. Zum Beispiel eignet sich für ein Hauptfenster das BorderLayout, im Bereich BorderLayout.NORTH würde sich ein Panel mit einem BoxLayout für die Werkzeugleiste wohl fühlen. Für eventuelle seitliche Abschnitte böte sich ebenfalls ein BoxLayout an, aber diesmal mit vertikaler Anordnung. Aber letzendlich kommt es auch immer auf die Anwendung an.
Auch wenn Sie einen GUI-Editor besitzen, sollten Sie sich zuerst mit den Layoutmanagern vertraut machen, da auch die GUI-Editoren auf eben diese aufbauen.

Weitere Layoutmanager

Dies waren bei Weitem nicht alle Layoutmanager. Sondern nur die, die am häufigsten verwendet werden. Es gibt zum Beispiel noch das GridBagLayout, dass ähnlich wie das GridLayout eine Tabelle zum Anordnen verwendet, aber etwas komplexer ist. Aus diesem Grund wird es in einem anderen Kapitel behandelt. Es gibt auch Layoutmanager, die nicht mit dem JDK geliefert werden, so beispielsweise das Forms-Layout von JGoodies. Mit dem GridBagLayout oder dem etwas einfacheren, aber nicht minder mächtigem Forms-Layout können Sie sich in den meisten Fällen auch das Verschachteln von Layoutmanagern sparen.
Daneben können Sie auch ihre eigenen Layoutmanager schreiben oder die vorhanden modifizieren, ein Beispiel dafür ist das ExtendedFlowLayout aus D) Einen Layout-Manager anpassen.

Weitere LayoutManager-Beispiele erklären wir Ihnen in unserem Artikel im Byte-Welt-Wiki.

Nur Buttons?

Bis jetzt haben Sie nur mit Buttons gearbeitet. Es ist klar, dass dies jetzt langweilig wird, aber mit Buttons kann man am besten die Layoutmanager kennenlernen und ausprobieren. Im nächsten Kapitel werden Sie aber zwei weitere wichtige Steuerelemente kennen: das Label und das Texfeld.

3 Replies to “13.02.03 Die Layoutmanager”

  1. Sebastian

    Erst einmal Danke für den tollen Blog!

    Ich glaub hier stimmt was nicht:

    component.setPrefferedSize(new Dimension(int x, int y));

    Bevorzugt heißt ja „preferred“, weshalb die Funktion vermutlich nicht setPrefferedSize(int x, int y) heißt, sondern setPreferredSize(x,y).

    MfG Sebastian

  2. Stefan Kiesel

    Hallo Sebastian,

    stimmt, da hat Fabian wohl einen kleinen Tippfehler eingebaut. Ich habe den Artikel ausgebessert.

    Danke für den Hinweis und Grüße
    Stefan

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.