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.