Bevor die vordefinierten Layouts von jowidgets vorgestellt
werden, soll vorab die ILayouter
Schnittstelle besprochen werden, um ein besseres Verständnis zu
schaffen, was beim Layouten eines Containers passiert.
Um einen eigenes (custom) Layout zu verwenden, muss diese Schnittstelle implementiert werden:
1 public interface ILayouter extends ILayoutDescriptor { 2 3 void layout(); 4 5 Dimension getMinSize(); 6 7 Dimension getPreferredSize(); 8 9 Dimension getMaxSize(); 10 11 void invalidate(); 12 13 }
Die Methode layout()
ist dafür zuständig, auf
den Controls eines Containers die Größe und die Position zu
setzen.
Die ClientArea
ist der Bereich des
Containers, welcher für das Zeichnen der Controls zur Verfügung
steht. Die DecoratedSize
ist die gesamte
Größe des Containers bei einer gegebenen
ClientArea
Größe. Bei einem Fenster kommen
zum Beispiel bei der DecoratedSize
noch der
Rahmen oder ein eventuelles Menü hinzu.
Die Methoden getMinSize()
,
getPreferredSize()
und
getMaxSize()
geben die minimale, bevorzugte
und maximale Größe des Containers zum aktuellen Zeitpunkt
zurück, die der Layouter benötigt, um seine Controls zu
layouten. Die zurückgegebenen Größen beziehen sich dabei auf die
DecoratedSize
und
nicht auf die Größe der
ClientArea
.
Die Methode invalidate()
wird aufgerufen,
wenn sich die Struktur des Layouts geändert haben
könnte. Dies kann ein guter Zeitpunkt sein,
um gecachte Werte zu löschen.
Die Verwendung der Schnittstelle soll Anhand eines Beispiels
verdeutlicht werden. Es wird ein vereinfachtes
Fill Layout implementiert.
Ein Fill Layout zeichnet das erste sichtbare Control eines
Containers so, dass es die ClientArea
voll
ausfüllt. Eine Implementierung könnte wie folgt aussehen:
1 final class FillLayout implements ILayouter { 2 3 private final IContainer container; 4 5 private Dimension minSize; 6 private Dimension preferredSize; 7 8 FillLayout(final IContainer container) { 9 this.container = container; 10 } 11 12 @Override 13 public void layout() { 14 final IControl control = getFirstVisibleControl(); 15 if (control != null) { 16 final Rectangle clientArea = container.getClientArea(); 17 control.setPosition(clientArea.getPosition()); 18 control.setSize(clientArea.getSize()); 19 } 20 } 21 22 @Override 23 public Dimension getMinSize() { 24 if (minSize == null) { 25 this.minSize = calcMinSize(); 26 } 27 return minSize; 28 } 29 30 @Override 31 public Dimension getPreferredSize() { 32 if (preferredSize == null) { 33 this.preferredSize = calcPreferredSize(); 34 } 35 return preferredSize; 36 } 37 38 @Override 39 public Dimension getMaxSize() { 40 return Dimension.MAX; 41 } 42 43 @Override 44 public void invalidate() { 45 minSize = null; 46 preferredSize = null; 47 } 48 49 private Dimension calcMinSize() { 50 final IControl control = getFirstVisibleControl(); 51 if (control != null) { 52 return container.computeDecoratedSize(control.getMinSize()); 53 } 54 else { 55 return container.computeDecoratedSize(new Dimension(0, 0)); 56 } 57 } 58 59 private Dimension calcPreferredSize() { 60 final IControl control = getFirstVisibleControl(); 61 if (control != null) { 62 return container.computeDecoratedSize(control.getPreferredSize()); 63 } 64 else { 65 return container.computeDecoratedSize(new Dimension(0, 0)); 66 } 67 } 68 69 private IControl getFirstVisibleControl() { 70 for (final IControl control : container.getChildren()) { 71 if (control.isVisible()) { 72 return control; 73 } 74 } 75 return null; 76 } 77 }
Folgendes ist dabei zu beachten:
Die Werte für die MinSize
und
PreferredSize
werden, solange
invalidate()
nicht aufgerufen wird, nur
ein Mal berechnet. Dadurch kann die Performance gesteigert
werden. Gerade bei verschachtelten Containern kann das
Berechnen der PreferredSize
unter
Umständen teuer sein.
Die Methoden calcMinSize()
und
calcPreferredSize()
verwenden die Methode
computeDecoratedSize()
des Containers um
aus der für die ClientArea
gültigen Große
die DecoratedSize
zu berechnen (Zeile 52,
55, 62, 65). Wird dies nicht berücksichtigt, funktioniert
das Layout nur für die Container korrekt, wo die
ClientArea
Größe mit der
DecoratedSize
übereinstimmt.
Um einen eigenen (custom) Layouter zu implementieren, ist es eventuell hilfreich, den Source Code der vordefinierten Layout Manager zu studieren. Dieser findet sich unter anderem hier.