Wie kann ein GridLayout geändert werden?
Java definiert den LayoutManager GridLayout, bei dem alle Komponenten rasterartig in gleich großen Feldern angeordnet werden. Die Reihenfolge der geladenen Komponenten verläuft dabei von oben zeilenweise jeweils in der für die Komponente festgelegten Richtung entweder von links nach rechts oder umgekehrt. Bleiben Zellen frei, so liegen diese demnach nebeneinander in der jeweils untersten Reihe.
Das folgende Beispiel zeigt dies anhand von sieben JButton
in einem GridLayout
mit drei Zeilen und
drei Spalten.
public class GridLayoutBsp { public GridLayoutBsp() { init(); } private void init() { JPanel panel = new JPanel(); panel.setLayout(new GridLayout(3,3)); panel.add(new JButton("Button eins")); panel.add(new JButton("Button zwei ohne Border")); panel.add(new JButton("Button drei")); panel.add(new JButton("Button vier")); panel.add(new JButton("Button f\u00fcnf")); panel.add(new JButton("Button sechs")); panel.add(new JButton("sieben")); JFrame frame = new JFrame(); frame.add(panel); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("Vertical Grid Layout"); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new GridLayoutBsp()); } }
Um den Verlauf der Komponenten von horizontal nach vertikal abzuändern, bieten sich zwei Möglichkeiten an.
- Durch Implementierung des Interface
LayoutManager
kann eine völlig neue Layout-Klasse geschrieben werden. - Durch Erweitern der Klasse
GridLayout
und Überschreiben vonpublic void layoutContainer(Container parent)
werden auf einfache Weise alle sonstigen Eigenschaften vonGridLayout
geerbt.
Da das beabsichtigte Ziel der Abänderung des GridLayout
,
abgesehen von der Reihenfolge, keine darüber
hinausgehende Änderung der Komponenten-Positionen
beinhaltet, bietet sich die Methode der Erweiterung an.
Hierzu
werden in der neuen Klasse die Konstruktoren
übernommen. Sie verweisen dann, durch Aufruf von super()
,
auf ihre Pendants in der Superklasse GridLayout
.
Wie bereits oben erwähnt muss dann nur noch layoutContainer()
überschrieben werden. In dieser Methode findet die
eigentliche Layout-Arbeit statt, die Positionierung
aller Komponenten des Zielcontainers.
Überschreiben von layoutContainer(Container
parent)
Die gesamte Routine steht in einem synchronized
-Block,
um das Zeichnen der Komponenten Thread-sicher zu
gestalten.
Hier werden zunächst die
Randabstände (Insets
) des Containers
ermittelt, bevor die folgenden Eigenschaften in
Variablen abgelegt werden:
- int ncomponents
- Anzahl der im Container enthaltenen Komponenten
- int nrows
- Zahl der durch das Layout zu erzeugenden Reihen
- int ncols
- Zahl der durch das Layout zu erzeugenden Spalten
- boolean ltr
-
Boolscher Wert für die Container-eigene
Orientierungsrichtung, in der Komponenten in ihm
gezeichnet werden;
true
wenn dies von links nach rechts erfolgt.
public class VerticalGridLayout extends GridLayout { public VerticalGridLayout() { super(); } public VerticalGridLayout(int rows, int cols) { super(rows, cols); } public VerticalGridLayout(int rows, int cols, int hgap, int vgap) { super(rows, cols, hgap, vgap); } public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int nrows = getRows(); int ncols = getColumns(); boolean ltr = parent.getComponentOrientation().isLeftToRight(); if (ncomponents == 0) { return; } if (nrows > 0) { ncols = (ncomponents + nrows - 1) / nrows; } else { nrows = (ncomponents + ncols - 1) / ncols; } // Fensterdimensionen ohne Kopfleiste int w = parent.getWidth() - (insets.left + insets.right); int h = parent.getHeight() - (insets.top + insets.bottom); // Platzbedarf einer Komponente mit gap w = (w - (ncols - 1) * getHgap()) / ncols; h = (h - (nrows - 1) * getVgap()) / nrows; if (ltr) { for (int c = 0, x = insets.left; c < ncols; c++, x += w + getHgap()) { for (int r = 0, y = insets.top; r < nrows; r++, y += h + getVgap()) { int i = c * nrows + r; if (i < ncomponents) { parent.getComponent(i).setBounds(x, y, w, h); } } } } else { for (int c = 0, x = parent.getWidth() - insets.right - w; c < ncols; c++, x -= w + getHgap()) { for (int r = 0, y = insets.top; r < nrows; r++, y += h + getVgap()) { int i = c * nrows + r; if (i < ncomponents) { parent.getComponent(i).setBounds(x, y, w, h); } } } } } } }
In der Elternklasse wird innerhalb der Konstruktoren
sichergestellt, dass mindestens eine der Angaben
für die Zeilen- und Spaltenzahl größer
null ist, somit können hieraus, unter
Berücksichtigung der Komponentenzahl, die
benötigen Anzahlen von Zeilen und Spalten berechnet
werden. Da alle Komponenten im GridLayout
in der selben Größe dargestellt werden, sind
dann aus der Containergröße die zur
Verfügung stehenden Breiten und Höhen der
einzelnen Komponenten zu ermittelt. Sie werden in den
beiden Variablen w
und h
abgelegt.
Bei der eigentlichen Positionierung der Komponenten wird
dann in zwei Blöcken zwischen der o.a.
Ladereihenfolge von links nach rechts bzw. der
gegenläufigen unterschieden, weil dies
natürlich Auswirkungen auf die horizontalen
Positionen der einzelnen Komponenten hat. Das
Grundprinzip der Berechnung bleibt jedoch ähnlich.
In der äußeren Schleife werden die
horizontalen, in der inneren die vertikalen Positionen
berechnet.
Für die Links-Rechts-Orientierung
wird in der äußeren Schleife der Startpunkt x
in der linken oberen Ecke des Containers unter
Berücksichtigung der Insets festgelegt. Der x-Wert
dieses Punktes wird bei jedem Schleifendurchlauf
schrittweise um eine Komponentenbreite und einen
horizontalen Zwischenraum incrementiert. In der inneren
Schleife wird Entsprechendes für den y-Wert der
vertikalen Position durchgeführt.
Die Berechnung des Array-Indexes der zu setzenden
Komponente geschieht dann folgendermaßen: Die
Komponenten werden spaltenweise gesetzt, sodass im
vorliegenden Beispiel mit drei Reihen und drei Spalten
die Indices der ersten Spalte 0, 1 und 2 lauten.
Multipliziert man mit diesen die Gesamtzahl der Reihen
(3) erhält man jeweils den Index des ersten Feldes
einer Spalte (0, 3, 6). Addiert man dazu jeweils die
Reihennummer, erhält man den endgültigen Index
der zu ladenden Komponente. Sie wird durch setBounds()
unter Übergabe der Parameter für Position und
Größe positioniert.
Zum Ausführen des Layouts reicht es dann aus, in der
oben gelisteten Klasse GridLayoutBsp
die
Zeile
panel.setLayout(new GridLayout(3,3));
durch
panel.setLayout(new VerticalGridLayout(3,3));
auszutauschen und den Frametitel anzupassen.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.