D) Einen Layout-Manager anpassen
Das Layout anpassen
Die Wurzel unseres Problems liegt in dieser Zeile:
dim.height = Math.max(dim.height, d.height);
Dadurch wird der Container immer nur so groß wie seine größte Komponente. Es gilt also die Berechnung der Größe des Containers an unsere Bedürfnisse anzupassen:
public Dimension preferredLayoutSize(Container target) { synchronized (target.getTreeLock()) { int width = 0; int height = 0; Dimension dim = new Dimension(0, 0); Insets insets = target.getInsets(); int nmembers = target.getComponentCount(); boolean firstVisibleComponent = true; for (int i = 0; i < nmembers; i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getPreferredSize(); if (firstVisibleComponent) { firstVisibleComponent = false; width = d.width; height = d.height; } else { if (width + d.width > target.getWidth() - insets.left - insets.right - getHgap() * 2) { dim.height += height + getVgap(); dim.width = Math.max(dim.width, width); width = d.width; height = d.height; } else { width += d.width + getHgap(); height = Math.max(d.height, height); } } } } dim.height += height + getVgap() * 2 + insets.top + insets.bottom; dim.width = Math.max(dim.width, width) + getHgap() * 2 + insets.left + insets.right; return dim; } }
Sie sehen, dass die Änderungen nur sehr gering gegenüber den Originalimplementierungen sind. Konkret:
- In Zeile 4 und 5 legen wir uns Variablen an, die die Größe des Containers speichern
- In Zeile 7 holen wir uns die
Insets
des Containers, also die Größe des Rahmens - In Zeile 16 und 17 (falls es sich um die erste, sichtbare Komponente handelt) setzen wir die aktuelle Größe auf die bevorzugte Höhe und Breite des Containers
- In Zeile 20 wird überprüft (falls es sich nicht um die erste, sichtbare Komponente handelt), ob die nächste Komponente in der selben Zeile die Breite des Containers überschreiten würde.
- In Zeile 21, 22, 23 und 24 wird – falls der vorherige Punkt zutrifft – die Höhe des Containers und ggf. auch die Breite (falls die aktuelle Komponente alleine breiter als der komplette Container ist) angepasst.
- In Zeile 27 und 28 werden die ganz normalen Schritte der Originalimplementierung ausgeführt – falls Zeile 20 nicht zutrifft
- In Zeile 33 und 34 wird nun noch die Größe des Containers um die Größe des Rahmens und den Platzhaltern zwischen den Komponenten am Anfang und am Ende erweitert.
Selbiges Verhalten müssen Sie nun noch für unsere andere, überschriebene Methode implementieren:
public Dimension minimumLayoutSize(Container target) { synchronized (target.getTreeLock()) { int width = 0; int height = 0; Dimension dim = new Dimension(0, 0); Insets insets = target.getInsets(); int nmembers = target.getComponentCount(); boolean firstVisibleComponent = true; for (int i = 0; i < nmembers; i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getMinimumSize(); if (firstVisibleComponent) { firstVisibleComponent = false; width = d.width; height = d.height; } else { if (width + d.width > target.getWidth() - insets.left - insets.right - getHgap() * 2) { dim.height += height + getVgap(); dim.width = Math.max(dim.width, width); width = d.width; height = d.height; } else { width += d.width + getHgap(); height = Math.max(d.height, height); } } } } dim.height += height + getVgap() * 2 + insets.top + insets.bottom; dim.width = Math.max(dim.width, width) + getHgap() * 2 + insets.left + insets.right; return dim; } }
Beim Betrachten des Quellcodes für die eigentliche Ausrichtung der Komponenten (layoutContainer
) stellen Sie fest, dass die Ausrichtung bereits die Höhe und Breite unseres Containers berücksichtigt und somit nicht noch extra angepasst werden muss. Es genügt völlig die Berechnung der Größe des Containers anzupassen.
Beachten Sie noch beim Anpassen eines Layout-Managers, dass Sie nicht auf ggf. vorhandene, private Attribute zugreifen können, sondern stattdessen die Getter- und Setter-Methoden verwenden müssen.
Gegenüberstellung
Um beide Layouts miteinander vergleichen zu können, erweitern wir unser Testprogramm ein wenig:
package de.jbb.tools; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class Test { public static void main(String[] args) { JFrame frame = new JFrame("ExtendedFlowLayout vs. FlowLayout"); JPanel normalFlow = new JPanel(); JPanel extendedFlow = new JPanel(); JLabel n1 = new JLabel("Das ist ein Label mit viel Text (FL)"); JLabel n2 = new JLabel("Das ist noch ein Label mit viel Text (FL)"); JLabel n3 = new JLabel("Und das ist ein drittes Label mit viel Text (FL)"); JLabel e1 = new JLabel("Das ist ein Label mit viel Text (EFL)"); JLabel e2 = new JLabel("Das ist noch ein Label mit viel Text (EFL)"); JLabel e3 = new JLabel("Und das ist ein drittes Label mit viel Text (EFL)"); n1.setBackground(Color.YELLOW); e1.setBackground(Color.YELLOW); n2.setBackground(Color.RED); e2.setBackground(Color.RED); n3.setBackground(Color.GREEN); e3.setBackground(Color.GREEN); n1.setOpaque(true); n2.setOpaque(true); n3.setOpaque(true); e1.setOpaque(true); e2.setOpaque(true); e3.setOpaque(true); frame.setLayout(new BorderLayout()); normalFlow.setLayout(new FlowLayout(FlowLayout.LEFT)); extendedFlow.setLayout(new ExtendedFlowLayout(FlowLayout.LEFT)); frame.add(normalFlow, BorderLayout.NORTH); frame.add(extendedFlow, BorderLayout.SOUTH); frame.add(new JLabel("Platzhalter"), BorderLayout.CENTER); normalFlow.add(n1); normalFlow.add(n2); normalFlow.add(n3); extendedFlow.add(e1); extendedFlow.add(e2); extendedFlow.add(e3); frame.setSize(450, 400); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
Wir erhalten folgendes Ergebnis:
Lesen Sie auf der nächsten Seite das Fazit und eine Zusammenfassung.
Super Artikel, löst gerade mein Problem!
Eigentlich hätte ich erwartet, dass das FlowLayout von Swing genau so funktioniert wie jetzt das ExtendedFlowLayout…