GroupLayout für Homosapiens: Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
K (Fortgeschrittenes:)
K
 
(10 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
== GroupLayout für Homosapiens: ==
+
= GroupLayout für Homosapiens: =
  
 
Das GroupLayout ist ein mächtiger LayoutManager, der leider oft als komplizierter missverstanden wird, als er eigentlich ist. Besonders GUI-Builder erzeugen meist sehr hässlichen Code mit dem Grouplayout, weshalb ich euch rate erst mal ein GroupLayout von Hand zu schreiben.
 
Das GroupLayout ist ein mächtiger LayoutManager, der leider oft als komplizierter missverstanden wird, als er eigentlich ist. Besonders GUI-Builder erzeugen meist sehr hässlichen Code mit dem Grouplayout, weshalb ich euch rate erst mal ein GroupLayout von Hand zu schreiben.
Zeile 7: Zeile 7:
 
Einem JPanel ein GroupLayout zu verpassen funktioniert erstmal nicht ganz genauso wie bei anderen LayoutManagern:
 
Einem JPanel ein GroupLayout zu verpassen funktioniert erstmal nicht ganz genauso wie bei anderen LayoutManagern:
  
<code=java>JPanel panel=new JPanel();
+
<syntaxhighlight lang="java">JPanel panel=new JPanel();
 
GroupLayout layout = new GroupLayout(panel);
 
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);</code=java>
+
panel.setLayout(layout);</syntaxhighlight>
  
 
Vorsicht: Das GroupLayout wirft sehr schnell mal Exceptions wenn bei der initialisierung etwas schief gelaufen ist (z.B ein Komponent vergessen). Davon nicht abschrecken lassen! Andere LayoutManager kaschieren manche Ungereimtheiten und zeigen trotzdem was an. Das GroupLayout ist da wohl eher auf Perfektionismus ausgelegt: richtig oder gar nicht.
 
Vorsicht: Das GroupLayout wirft sehr schnell mal Exceptions wenn bei der initialisierung etwas schief gelaufen ist (z.B ein Komponent vergessen). Davon nicht abschrecken lassen! Andere LayoutManager kaschieren manche Ungereimtheiten und zeigen trotzdem was an. Das GroupLayout ist da wohl eher auf Perfektionismus ausgelegt: richtig oder gar nicht.
  
  
== Zu den Komponenten: ==
+
= Zu den Komponenten: =
  
 
Auch hier geht das GroupLayout wieder einen eigenen Weg: Komponenten werden nicht per panel.add(Component c) "hinzugefügt".
 
Auch hier geht das GroupLayout wieder einen eigenen Weg: Komponenten werden nicht per panel.add(Component c) "hinzugefügt".
Zeile 22: Zeile 22:
 
Beispiel: Wir imitieren ein FlowLayout mit 3 Labels:
 
Beispiel: Wir imitieren ein FlowLayout mit 3 Labels:
  
<code=java>JLabel label1=new JLabel("1");
+
<syntaxhighlight lang="java">JLabel label1=new JLabel("1");
 
JLabel label2=new JLabel("2");
 
JLabel label2=new JLabel("2");
JLabel label3=new JLabel("3");</code=java>
+
JLabel label3=new JLabel("3");</syntaxhighlight>
  
  
=== 1. Beziehungen auf vertikaler Ebene: ===
+
== 1. Beziehungen auf vertikaler Ebene: ==
  
 
Wir wollen, dass alle 3 Labels in der gleichen Zeile stehen. Sehen wir von "Norden" aus auf unser Layout drauf (man beuge sich über den Bildschirm und blicke nach unten), sind die 3 Labels nebeneinander. Wenn wir 2 oder mehr Komponenten nebeneinander ausrichten wollen, benötigen wir eine ParallelGroup:
 
Wir wollen, dass alle 3 Labels in der gleichen Zeile stehen. Sehen wir von "Norden" aus auf unser Layout drauf (man beuge sich über den Bildschirm und blicke nach unten), sind die 3 Labels nebeneinander. Wenn wir 2 oder mehr Komponenten nebeneinander ausrichten wollen, benötigen wir eine ParallelGroup:
  
<code=java>ParallelGroup verticalGroup=layout.createParallelGroup();</code=java>
+
<syntaxhighlight lang="java">ParallelGroup verticalGroup=layout.createParallelGroup();</syntaxhighlight>
  
 
Dieser ParallelGroup werden jetzt die Komponenten hinzugefügt:
 
Dieser ParallelGroup werden jetzt die Komponenten hinzugefügt:
  
<code=java>verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);</code=java>
+
<syntaxhighlight lang="java">verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);</syntaxhighlight>
  
  
=== 2. Beziehungen auf horizontaler Ebene: ===
+
== 2. Beziehungen auf horizontaler Ebene: ==
  
 
Sehen wir von "Westen" auf unser Layout (man stelle sich links neben den Bildschirm und blicke hinein), sind die 3 Labels hintereinander. Dafür gibts die SequentialGroup:
 
Sehen wir von "Westen" auf unser Layout (man stelle sich links neben den Bildschirm und blicke hinein), sind die 3 Labels hintereinander. Dafür gibts die SequentialGroup:
  
<code=java>SequentialGroup horizontalGroup=layout.createSequentialGroup;
+
<syntaxhighlight lang="java">SequentialGroup horizontalGroup=layout.createSequentialGroup;
 
   
 
   
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);</code=java>
+
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);</syntaxhighlight>
  
 
Zum Abschluss müssen wir unsere erstellten Gruppen noch dem Layout mit folgenden Methoden zuweisen:
 
Zum Abschluss müssen wir unsere erstellten Gruppen noch dem Layout mit folgenden Methoden zuweisen:
  
<code=java>layout.setVerticalGroup(verticalGroup);
+
<syntaxhighlight lang="java">layout.setVerticalGroup(verticalGroup);
layout.setHorizontalGroup(horizontalGroup);</code=java>
+
layout.setHorizontalGroup(horizontalGroup);</syntaxhighlight>
  
 
Jetzt noch panel in einen JFrame packen und fertig, unser erstes GroupLayout läuft!  
 
Jetzt noch panel in einen JFrame packen und fertig, unser erstes GroupLayout läuft!  
Zeile 57: Zeile 57:
 
'''Wichtig:''' Wenn du einen Komponenten nur zu einer Group addest, fliegt eine Exception und es wird nichts angezeigt!
 
'''Wichtig:''' Wenn du einen Komponenten nur zu einer Group addest, fliegt eine Exception und es wird nichts angezeigt!
  
== VerticalFlowLayout ==
+
= VerticalFlowLayout =
  
 
Aber was nützt uns ein FlowLayout? Schon so mancher hat den Bedarf nach einem VerticalFlowLayout angemeldet. Mit GroupLayout kein Problem: wir vertauschen einfach vertical- und horizontalGroup.
 
Aber was nützt uns ein FlowLayout? Schon so mancher hat den Bedarf nach einem VerticalFlowLayout angemeldet. Mit GroupLayout kein Problem: wir vertauschen einfach vertical- und horizontalGroup.
  
SequentialGroup verticalGroup=layout.createSequentialGroup();
+
<syntaxhighlight lang="java">SequentialGroup verticalGroup=layout.createSequentialGroup();
 
verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);
 
verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);
  
 
ParallelGroup horizontalGroup=layout.createParallelGroup;
 
ParallelGroup horizontalGroup=layout.createParallelGroup;
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);
+
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);</syntaxhighlight>
  
  
 
[[Datei:GLTut-2-VerticalFlowLayout.png]]
 
[[Datei:GLTut-2-VerticalFlowLayout.png]]
  
== Standard GUI ==
+
= Standard GUI =
  
 
Wie sieht es nun mit komplexeren Masken aus? Ich habe früher versucht aus zig verschiedenen, verschachtelten Panels mit jeweils eigenem einfachen LayoutManagern eine GUI zusammenzubasteln. Der Aufwand war enorm, die Übersichtlichkeit schrecklich, es war fehleranfällig und meist kam sowieso nicht das raus was gewollt war.
 
Wie sieht es nun mit komplexeren Masken aus? Ich habe früher versucht aus zig verschiedenen, verschachtelten Panels mit jeweils eigenem einfachen LayoutManagern eine GUI zusammenzubasteln. Der Aufwand war enorm, die Übersichtlichkeit schrecklich, es war fehleranfällig und meist kam sowieso nicht das raus was gewollt war.
Zeile 79: Zeile 79:
 
[[Datei:GLTut-3-SchoeneGui.png]]
 
[[Datei:GLTut-3-SchoeneGui.png]]
  
=== 1. Vertikale Group: ===
+
== 1. Vertikale Group: ==
  
 
Wir stellen uns wieder vor, wir blicken von oben in unser Layout rein. Jetzt gibt’s zwei Betrachtungsweisen: Haben wir 3 Zeilen hintereinander, in denen je ein Label, ein Textfeld und ein Button nebeneinander stehen oder haben wir 3 Spalten nebeneinander mit jeweils 3 Labels, Textfeldern und Buttons hintereinander?
 
Wir stellen uns wieder vor, wir blicken von oben in unser Layout rein. Jetzt gibt’s zwei Betrachtungsweisen: Haben wir 3 Zeilen hintereinander, in denen je ein Label, ein Textfeld und ein Button nebeneinander stehen oder haben wir 3 Spalten nebeneinander mit jeweils 3 Labels, Textfeldern und Buttons hintereinander?
 
Die Entscheidung bleibt dir überlassen, ich werde es hier anhand des „3 Zeilen“ Ansatzes demonstrieren:
 
Die Entscheidung bleibt dir überlassen, ich werde es hier anhand des „3 Zeilen“ Ansatzes demonstrieren:
  
<code=java>SequentialGroup verticalGroup=layout.createSequentialGroup();
+
<syntaxhighlight lang="java">SequentialGroup verticalGroup=layout.createSequentialGroup();
 
          
 
          
 
         verticalGroup.addGroup(layout.createParallelGroup()
 
         verticalGroup.addGroup(layout.createParallelGroup()
Zeile 94: Zeile 94:
 
         verticalGroup.addGroup(layout.createParallelGroup()
 
         verticalGroup.addGroup(layout.createParallelGroup()
 
                 .addComponent(label3).addComponent(textField3).addComponent(button3)
 
                 .addComponent(label3).addComponent(textField3).addComponent(button3)
                 );</code=java>
+
                 );</syntaxhighlight>
  
  
=== 2. Horizontale Group ===
+
== 2. Horizontale Group ==
  
 
Analog zur Vertikalen Group: Haben wir 3 Zeilen nebeneinander in denen Label, Textfeld und Button hintereinander stehen oder haben wir 3 Spalten hintereinander, in denen untereinander je 3 Labels, Textfelder oder Buttons stehen?
 
Analog zur Vertikalen Group: Haben wir 3 Zeilen nebeneinander in denen Label, Textfeld und Button hintereinander stehen oder haben wir 3 Spalten hintereinander, in denen untereinander je 3 Labels, Textfelder oder Buttons stehen?
 
W man sich beim Vertikalen Teil für die Zeilen Denkweise entschieden hat, ist es mMn sinnvoll dabei zu bleiben:
 
W man sich beim Vertikalen Teil für die Zeilen Denkweise entschieden hat, ist es mMn sinnvoll dabei zu bleiben:
  
<code=java>ParallelGroup horizontalGroup=layout.createParallelGroup();
+
<syntaxhighlight lang="java">ParallelGroup horizontalGroup=layout.createParallelGroup();
 
          
 
          
 
   
 
   
Zeile 113: Zeile 113:
 
         horizontalGroup.addGroup(layout.createSequentialGroup()
 
         horizontalGroup.addGroup(layout.createSequentialGroup()
 
                 .addComponent(label3).addComponent(textField3).addComponent(button3)
 
                 .addComponent(label3).addComponent(textField3).addComponent(button3)
                 );</code=java>
+
                 );</syntaxhighlight>
  
 
In meinem StandardFrame mit fixer Größe sieht das ganze nun aber so aus:
 
In meinem StandardFrame mit fixer Größe sieht das ganze nun aber so aus:
Zeile 121: Zeile 121:
 
Die Komponenten füllen den gesamten Platz der aus – wir brauchen soetwas wie den Glue aus dem BoxLayout. Im GroupLayout heißt das Gap was die Sache mMn auch besser beschreibt (wer will denn Kleber in seiner GUI?).
 
Die Komponenten füllen den gesamten Platz der aus – wir brauchen soetwas wie den Glue aus dem BoxLayout. Im GroupLayout heißt das Gap was die Sache mMn auch besser beschreibt (wer will denn Kleber in seiner GUI?).
  
== Gaps und Größenangaben ==
+
= Gaps und Größenangaben =
  
Erstens gibt’s da mal eine Komfortfunktion die man ich eigentlich immer aktiviert habe:
+
Erstens gibt’s da mal eine Komfortfunktion die ich eigentlich immer aktiviert habe:
  
<code=java>layout.setAutoCreateGaps(true);</code=java>
+
<syntaxhighlight lang="java">layout.setAutoCreateGaps(true);</syntaxhighlight>
  
 
Damit kleben die Komponenten nicht mehr so aneinander, wie man das auch vom GridLayout her kennt.
 
Damit kleben die Komponenten nicht mehr so aneinander, wie man das auch vom GridLayout her kennt.
Zeile 131: Zeile 131:
 
Zu unserm Problem, wir können Gaps genauso wie Komponenten einer Gruppe hinzufügen:
 
Zu unserm Problem, wir können Gaps genauso wie Komponenten einer Gruppe hinzufügen:
  
<code=java>verticalGroup.addGap(Short.MAX_VALUE);</code=java>
+
<syntaxhighlight lang="java">verticalGroup.addGap(Short.MAX_VALUE);</syntaxhighlight>
  
 
addGap erwartet einen Integer Wert als Parameter, der angibt wie groß der Gap sein soll. Short.MAX_VALUE liegt bei ca. 32000, so große Displays gibt’s noch nicht.
 
addGap erwartet einen Integer Wert als Parameter, der angibt wie groß der Gap sein soll. Short.MAX_VALUE liegt bei ca. 32000, so große Displays gibt’s noch nicht.
Zeile 140: Zeile 140:
 
Ändern wir den Code auf
 
Ändern wir den Code auf
  
<code=java>verticalGroup.addGroup(layout.createParallelGroup()
+
<syntaxhighlight lang="java">verticalGroup.addGroup(layout.createParallelGroup()
 
.addComponent(label1)
 
.addComponent(label1)
 
. addComponent(textField1,20,20,20)
 
. addComponent(textField1,20,20,20)
 
.addComponent(button1)
 
.addComponent(button1)
);</code=java>
+
);</syntaxhighlight>
  
 
Wird unser Textfield1 immer genau 20 Pixel hoch sein.
 
Wird unser Textfield1 immer genau 20 Pixel hoch sein.
Zeile 152: Zeile 152:
 
GroupLayout.DEFAULT_SIZE und GroupLayout.PREFERRED_SIZE
 
GroupLayout.DEFAULT_SIZE und GroupLayout.PREFERRED_SIZE
  
<code=java>verticalGroup.addGroup(layout.createParallelGroup()
+
<syntaxhighlight lang="java">verticalGroup.addGroup(layout.createParallelGroup()
 
                 .addComponent(label1)
 
                 .addComponent(label1)
 
                 .addComponent(textField1,GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
 
                 .addComponent(textField1,GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
 
                 .addComponent(button1)
 
                 .addComponent(button1)
                 );</code=java>
+
                 );</syntaxhighlight>
  
 
Jetzt kann das Textfeld maximal die Höhe seiner PreferredSize haben.
 
Jetzt kann das Textfeld maximal die Höhe seiner PreferredSize haben.
Zeile 168: Zeile 168:
 
Wir fügen einen Gap zwischen Zeile 3 und dem Ok Button ein:
 
Wir fügen einen Gap zwischen Zeile 3 und dem Ok Button ein:
  
<code=java>verticalGroup.addGap(0,0,Short.MAX_VALUE);
+
<syntaxhighlight lang="java">verticalGroup.addGap(0,0,Short.MAX_VALUE);
 
verticalGroup.addComponent(okButton);
 
verticalGroup.addComponent(okButton);
 
   
 
   
 
horizontalGroup.addGroup(layout.createSequentialGroup()
 
horizontalGroup.addGroup(layout.createSequentialGroup()
 
                 .addGap(0,0,Short.MAX_VALUE)
 
                 .addGap(0,0,Short.MAX_VALUE)
                 .addComponent(okButton));</code=java>
+
                 .addComponent(okButton));</syntaxhighlight>
  
 
ein (0,0,Short.MAX_VALUE) Gap füllt soviel Platz aus, wie ihm zur Verfügung steht. Egal wie groß ihr das Fenster macht, der OK Button ist immer rechts unten.
 
ein (0,0,Short.MAX_VALUE) Gap füllt soviel Platz aus, wie ihm zur Verfügung steht. Egal wie groß ihr das Fenster macht, der OK Button ist immer rechts unten.
Zeile 179: Zeile 179:
 
 
  
== Fortgeschrittenes: ==
+
= Fortgeschrittenes: =
  
  
Zeile 188: Zeile 188:
 
Erstmal setzten wir ganz normal die Preferred Size der Textfelder und die Font des Labels:
 
Erstmal setzten wir ganz normal die Preferred Size der Textfelder und die Font des Labels:
  
<code=java>Font f = new Font( "Arial", Font.PLAIN, 80 );
+
<syntaxhighlight lang="java">Font f = new Font( "Arial", Font.PLAIN, 80 );
 
label1.setFont(f);
 
label1.setFont(f);
 
   
 
   
 
textField1.setPreferredSize(new Dimension(70,20));
 
textField1.setPreferredSize(new Dimension(70,20));
 
textField2.setPreferredSize(new Dimension(100,20));
 
textField2.setPreferredSize(new Dimension(100,20));
textField3.setPreferredSize(new Dimension(130,20));</code=java>
+
textField3.setPreferredSize(new Dimension(130,20));</syntaxhighlight>
  
 
'''Problem 1:''' Die Komponenten einer Zeile der Höhe nach mittig ausrichten:
 
'''Problem 1:''' Die Komponenten einer Zeile der Höhe nach mittig ausrichten:
 
ParallelGroups kann man beim erzeugen einen Alignment Parameter übergeben. (Alignment. LEADING, TRAILING, CENTER oder BASELINE
 
ParallelGroups kann man beim erzeugen einen Alignment Parameter übergeben. (Alignment. LEADING, TRAILING, CENTER oder BASELINE
  
<code=java>verticalGroup.addGroup(layout.createParallelGroup(Alignment.CENTER)
+
<syntaxhighlight lang="java">verticalGroup.addGroup(layout.createParallelGroup(Alignment.CENTER)
 
.addComponent(label1)
 
.addComponent(label1)
 
.addComponent(textField1,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
 
.addComponent(textField1,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
 
.addComponent(button1)
 
.addComponent(button1)
);</code=java>
+
);</syntaxhighlight>
  
 
'''Problem 2:''' Quasi ein Tabstopp für die Buttons. Wir haben nicht mehr einfach 4 Zeilen, wir haben 4 Zeilen und 2 Spalten.  
 
'''Problem 2:''' Quasi ein Tabstopp für die Buttons. Wir haben nicht mehr einfach 4 Zeilen, wir haben 4 Zeilen und 2 Spalten.  
Zeile 216: Zeile 216:
 
3 Zeilen übereinander mit je Label und Textfeld hintereinander oder 3 Labels übereinander, danach 3 Textfelder übereinander. (Wir machens auf die Zeilen Art).
 
3 Zeilen übereinander mit je Label und Textfeld hintereinander oder 3 Labels übereinander, danach 3 Textfelder übereinander. (Wir machens auf die Zeilen Art).
  
<code=java>ParallelGroup labelTextfieldGroup=layout.createParallelGroup()
+
<syntaxhighlight lang="java">ParallelGroup labelTextfieldGroup=layout.createParallelGroup()
 
                 .addGroup(layout.createSequentialGroup()
 
                 .addGroup(layout.createSequentialGroup()
 
                 .addComponent(label1)
 
                 .addComponent(label1)
Zeile 228: Zeile 228:
 
                 .addComponent(label3)
 
                 .addComponent(label3)
 
                 .addComponent(textField3,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
 
                 .addComponent(textField3,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
                 );</code=java>
+
                 );</syntaxhighlight>
  
 
Dann noch die 4 Buttons übereinander, das ist wieder einfacher:
 
Dann noch die 4 Buttons übereinander, das ist wieder einfacher:
  
<code=java>ParallelGroup buttonGroup=layout.createParallelGroup(Alignment.TRAILING)
+
<syntaxhighlight lang="java">ParallelGroup buttonGroup=layout.createParallelGroup(Alignment.TRAILING)
 
         .addComponent(button1)
 
         .addComponent(button1)
 
         .addComponent(button2)
 
         .addComponent(button2)
 
         .addComponent(button3)
 
         .addComponent(button3)
         .addComponent(okButton);</code=java>
+
         .addComponent(okButton);</syntaxhighlight>
  
 
Jetzt stöpseln wir die zwei roten Rechteck-Gruppen zusammen:
 
Jetzt stöpseln wir die zwei roten Rechteck-Gruppen zusammen:
  
<code=java>horizontalGroup.addGroup(labelTextfieldGroup).addGroup(buttonGroup);</code=java>
+
<syntaxhighlight lang="java">horizontalGroup.addGroup(labelTextfieldGroup).addGroup(buttonGroup);</syntaxhighlight>
  
 
'''Problem 3:''' Den Buttons die gleiche Breite verpassen.
 
'''Problem 3:''' Den Buttons die gleiche Breite verpassen.
Zeile 246: Zeile 246:
 
Dafür gibt’s die Funktion linkSize:
 
Dafür gibt’s die Funktion linkSize:
  
<code=java>layout.linkSize(SwingConstants.HORIZONTAL,button1,button2,button3,okButton);</code=java>
+
<syntaxhighlight lang="java">layout.linkSize(SwingConstants.HORIZONTAL,button1,button2,button3,okButton);</syntaxhighlight>
  
 
SwingConstants.HORIZONTAL gibt an ob man die Breite oder die Höhe verlinken will. Danach kann man beliebig viele Komponenten hinzuschreiben (varargs).
 
SwingConstants.HORIZONTAL gibt an ob man die Breite oder die Höhe verlinken will. Danach kann man beliebig viele Komponenten hinzuschreiben (varargs).
  
== Weiterführende Links: ==
+
= Ein vollständiges Beispiel =
 +
An Hand eines Beispiels wollen wir noch die Flexibilität eines [[GroupLayout Demo|GroupLayouts demonstrieren]].
  
API-Dokumentation vom [http://docs.oracle.com/javase/7/docs/api/javax/swing/GroupLayout.html GroupLayout]  
+
= Weiterführende Links: =
<br />
+
 
Oracle: [http://download.oracle.com/javase/tutorial/uiswing/layout/group.html How to Use GroupLayout (The Java™ Tutorials > Creating a GUI With JFC/Swing > Laying Out Components]
+
*API-Dokumentation vom [http://docs.oracle.com/javase/7/docs/api/javax/swing/GroupLayout.html GroupLayout]  
 +
*Oracle: [http://download.oracle.com/javase/tutorial/uiswing/layout/group.html How to Use GroupLayout (The Java™ Tutorials > Creating a GUI With JFC/Swing > Laying Out Components]
 +
*[[LayoutManager]]
  
 
--[[Benutzer: bERt0r|bERt0r]] 04:06, 16. Dez 2011 (CET)
 
--[[Benutzer: bERt0r|bERt0r]] 04:06, 16. Dez 2011 (CET)
  
[[Kategorie:Java]]
+
[[Kategorie:LayoutManager]]
 
[[Kategorie:Swing]]
 
[[Kategorie:Swing]]

Aktuelle Version vom 2. April 2018, 15:57 Uhr

GroupLayout für Homosapiens:

Das GroupLayout ist ein mächtiger LayoutManager, der leider oft als komplizierter missverstanden wird, als er eigentlich ist. Besonders GUI-Builder erzeugen meist sehr hässlichen Code mit dem Grouplayout, weshalb ich euch rate erst mal ein GroupLayout von Hand zu schreiben. GroupLayout wurde mit dem Ziel geschaffen, von GUI Buildern verwendet zu werden, von Hand programmiert, liegt der Schreibaufwand etwa wie beim GridBagLayout.


Einem JPanel ein GroupLayout zu verpassen funktioniert erstmal nicht ganz genauso wie bei anderen LayoutManagern:

JPanel panel=new JPanel();
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);

Vorsicht: Das GroupLayout wirft sehr schnell mal Exceptions wenn bei der initialisierung etwas schief gelaufen ist (z.B ein Komponent vergessen). Davon nicht abschrecken lassen! Andere LayoutManager kaschieren manche Ungereimtheiten und zeigen trotzdem was an. Das GroupLayout ist da wohl eher auf Perfektionismus ausgelegt: richtig oder gar nicht.


Zu den Komponenten:

Auch hier geht das GroupLayout wieder einen eigenen Weg: Komponenten werden nicht per panel.add(Component c) "hinzugefügt". Man fasst die Komponenten in Gruppen zusammen und setzt diese in Beziehungen zueinander. Anders als bei den GridBagConstraints wurde beim GroupLayout dieses "Beziehungen setzen" aber zweigeteilt: Man gibt die Größenverhältnisse einmal vertikal und einmal horizontal an:

Beispiel: Wir imitieren ein FlowLayout mit 3 Labels:

JLabel label1=new JLabel("1");
JLabel label2=new JLabel("2");
JLabel label3=new JLabel("3");


1. Beziehungen auf vertikaler Ebene:

Wir wollen, dass alle 3 Labels in der gleichen Zeile stehen. Sehen wir von "Norden" aus auf unser Layout drauf (man beuge sich über den Bildschirm und blicke nach unten), sind die 3 Labels nebeneinander. Wenn wir 2 oder mehr Komponenten nebeneinander ausrichten wollen, benötigen wir eine ParallelGroup:

ParallelGroup verticalGroup=layout.createParallelGroup();

Dieser ParallelGroup werden jetzt die Komponenten hinzugefügt:

verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);


2. Beziehungen auf horizontaler Ebene:

Sehen wir von "Westen" auf unser Layout (man stelle sich links neben den Bildschirm und blicke hinein), sind die 3 Labels hintereinander. Dafür gibts die SequentialGroup:

SequentialGroup horizontalGroup=layout.createSequentialGroup;
 
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);

Zum Abschluss müssen wir unsere erstellten Gruppen noch dem Layout mit folgenden Methoden zuweisen:

layout.setVerticalGroup(verticalGroup);
layout.setHorizontalGroup(horizontalGroup);

Jetzt noch panel in einen JFrame packen und fertig, unser erstes GroupLayout läuft!

GLTut-1-FlowLayout.png

Wichtig: Wenn du einen Komponenten nur zu einer Group addest, fliegt eine Exception und es wird nichts angezeigt!

VerticalFlowLayout

Aber was nützt uns ein FlowLayout? Schon so mancher hat den Bedarf nach einem VerticalFlowLayout angemeldet. Mit GroupLayout kein Problem: wir vertauschen einfach vertical- und horizontalGroup.

SequentialGroup verticalGroup=layout.createSequentialGroup();
verticalGroup.addComponent(label1).addComponent(label2).addComponent(label3);

ParallelGroup horizontalGroup=layout.createParallelGroup;
horizontalGroup.addComponent(label1).addComponent(label2).addComponent(label3);


GLTut-2-VerticalFlowLayout.png

Standard GUI

Wie sieht es nun mit komplexeren Masken aus? Ich habe früher versucht aus zig verschiedenen, verschachtelten Panels mit jeweils eigenem einfachen LayoutManagern eine GUI zusammenzubasteln. Der Aufwand war enorm, die Übersichtlichkeit schrecklich, es war fehleranfällig und meist kam sowieso nicht das raus was gewollt war. Mit dem GroupLayout kann man sich das Panel verschachteln sparen, man verschachtelt die Groups. Das schafft zwar das Problem des Verschachtelns nicht aus der Welt, man kann sich aber besser auf die eine Sache konzentrieren weil man sich eine Menge Panels spart.

Beispiel: Wir erweitern unser VerticalFlowGroupLayout: Da soll in jede Zeile noch zusätzlich ein Textfeld und ein Button rein:

GLTut-3-SchoeneGui.png

1. Vertikale Group:

Wir stellen uns wieder vor, wir blicken von oben in unser Layout rein. Jetzt gibt’s zwei Betrachtungsweisen: Haben wir 3 Zeilen hintereinander, in denen je ein Label, ein Textfeld und ein Button nebeneinander stehen oder haben wir 3 Spalten nebeneinander mit jeweils 3 Labels, Textfeldern und Buttons hintereinander? Die Entscheidung bleibt dir überlassen, ich werde es hier anhand des „3 Zeilen“ Ansatzes demonstrieren:

SequentialGroup verticalGroup=layout.createSequentialGroup();
        
        verticalGroup.addGroup(layout.createParallelGroup()
                .addComponent(label1).addComponent(textField1).addComponent(button1)
                );
        verticalGroup.addGroup(layout.createParallelGroup()
                .addComponent(label2).addComponent(textField2).addComponent(button2)
                );
        verticalGroup.addGroup(layout.createParallelGroup()
                .addComponent(label3).addComponent(textField3).addComponent(button3)
                );


2. Horizontale Group

Analog zur Vertikalen Group: Haben wir 3 Zeilen nebeneinander in denen Label, Textfeld und Button hintereinander stehen oder haben wir 3 Spalten hintereinander, in denen untereinander je 3 Labels, Textfelder oder Buttons stehen? W man sich beim Vertikalen Teil für die Zeilen Denkweise entschieden hat, ist es mMn sinnvoll dabei zu bleiben:

ParallelGroup horizontalGroup=layout.createParallelGroup();
        
 
        horizontalGroup.addGroup(layout.createSequentialGroup()
                .addComponent(label1).addComponent(textField1).addComponent(button1)
                );
        horizontalGroup.addGroup(layout.createSequentialGroup()
                .addComponent(label2).addComponent(textField2).addComponent(button2)
                );
        horizontalGroup.addGroup(layout.createSequentialGroup()
                .addComponent(label3).addComponent(textField3).addComponent(button3)
                );

In meinem StandardFrame mit fixer Größe sieht das ganze nun aber so aus:

GLTut-4-HaesslicheGui.png

Die Komponenten füllen den gesamten Platz der aus – wir brauchen soetwas wie den Glue aus dem BoxLayout. Im GroupLayout heißt das Gap was die Sache mMn auch besser beschreibt (wer will denn Kleber in seiner GUI?).

Gaps und Größenangaben

Erstens gibt’s da mal eine Komfortfunktion die ich eigentlich immer aktiviert habe:

layout.setAutoCreateGaps(true);

Damit kleben die Komponenten nicht mehr so aneinander, wie man das auch vom GridLayout her kennt.

Zu unserm Problem, wir können Gaps genauso wie Komponenten einer Gruppe hinzufügen:

verticalGroup.addGap(Short.MAX_VALUE);

addGap erwartet einen Integer Wert als Parameter, der angibt wie groß der Gap sein soll. Short.MAX_VALUE liegt bei ca. 32000, so große Displays gibt’s noch nicht.

Man kann addGap aber auch 3 ints übergeben: addGap(int min,int pref, int max) Man kann also Minimum, Preferred und Maximum Size einstellen. Das funktioniert genauso bei Komponenten:

Ändern wir den Code auf

verticalGroup.addGroup(layout.createParallelGroup()
.addComponent(label1)
. addComponent(textField1,20,20,20)
.addComponent(button1)
);

Wird unser Textfield1 immer genau 20 Pixel hoch sein. Achtung: Die Größenangaben beziehen sich immer auf die Achse, in der sie gemacht werden. Angaben in der VerticalGroup beeinflussen nur die Höhe, Angaben in der HorizontalGroup nur die Breite.

Die Größenangaben manuell anzugeben ist allerdings kein sauberer Stil. Komponenten haben ja selber schon min, preferred und max Size Einstellungen, die werden dadurch einfach ignoriert. Um dem GroupLayout zu sagen, es soll immer auf eine dieser Methoden zurückgreifen gibt’s 2 Konstanten: GroupLayout.DEFAULT_SIZE und GroupLayout.PREFERRED_SIZE

verticalGroup.addGroup(layout.createParallelGroup()
                .addComponent(label1)
                .addComponent(textField1,GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
                .addComponent(button1)
                );

Jetzt kann das Textfeld maximal die Höhe seiner PreferredSize haben.

GLTut-5-TextfeldHoehe.png

Beispiel: Wir wollen links unten in unserem Fenster einen OK Button einfügen.

GLTut-6-Okbutton.png

Wir fügen einen Gap zwischen Zeile 3 und dem Ok Button ein:

verticalGroup.addGap(0,0,Short.MAX_VALUE);
verticalGroup.addComponent(okButton);
 
horizontalGroup.addGroup(layout.createSequentialGroup()
                .addGap(0,0,Short.MAX_VALUE)
                .addComponent(okButton));

ein (0,0,Short.MAX_VALUE) Gap füllt soviel Platz aus, wie ihm zur Verfügung steht. Egal wie groß ihr das Fenster macht, der OK Button ist immer rechts unten.

Fortgeschrittenes:

Beispiel: Wir stellen die Font eines unserer Labels auf 80 und wollen, dass die Textfeld und Button in der Mitte der Zeile angezeigt werden. Wir setzten für unsere Textfelder unterschiedliche Preferred Sizes und wollen dass die Buttons alle schön untereinander stehen und gleich breit sein. Ausserdem soll das Layout nur so viel Platz wie nötig ausfüllen.

GLTut-7-Fortgeschritten.png

Erstmal setzten wir ganz normal die Preferred Size der Textfelder und die Font des Labels:

Font f = new Font( "Arial", Font.PLAIN, 80 );
label1.setFont(f);
 
textField1.setPreferredSize(new Dimension(70,20));
textField2.setPreferredSize(new Dimension(100,20));
textField3.setPreferredSize(new Dimension(130,20));

Problem 1: Die Komponenten einer Zeile der Höhe nach mittig ausrichten: ParallelGroups kann man beim erzeugen einen Alignment Parameter übergeben. (Alignment. LEADING, TRAILING, CENTER oder BASELINE

verticalGroup.addGroup(layout.createParallelGroup(Alignment.CENTER)
.addComponent(label1)
.addComponent(textField1,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
.addComponent(button1)
);

Problem 2: Quasi ein Tabstopp für die Buttons. Wir haben nicht mehr einfach 4 Zeilen, wir haben 4 Zeilen und 2 Spalten.

GLTut-8-Rechtecke.png

Die HorizontalGroup muss deshalb verändert werden: Wir stellen uns vor, wir schauen von „Westen“/Links auf unser Layout. Da wir in unserem Layout 2 Spalten haben (Rote Rechtecke), die klar abgegrenzt hintereinander kommen sollen Brauchen wir erstmal eine SequentialGroup. SequentialGroup horizontalGroup=layout.createSequentialGroup();

Jetzt betrachten wir erstmal nur das linke Rechteck (um das rechte kümmern wir uns nachher). 3 Zeilen übereinander mit je Label und Textfeld hintereinander oder 3 Labels übereinander, danach 3 Textfelder übereinander. (Wir machens auf die Zeilen Art).

ParallelGroup labelTextfieldGroup=layout.createParallelGroup()
                .addGroup(layout.createSequentialGroup()
                .addComponent(label1)
                .addComponent(textField1,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
                )
                .addGroup(layout.createSequentialGroup()
                .addComponent(label2)
                .addComponent(textField2,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
                )
                .addGroup(layout.createSequentialGroup()
                .addComponent(label3)
                .addComponent(textField3,GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE,GroupLayout.PREFERRED_SIZE)
                );

Dann noch die 4 Buttons übereinander, das ist wieder einfacher:

ParallelGroup buttonGroup=layout.createParallelGroup(Alignment.TRAILING)
        .addComponent(button1)
        .addComponent(button2)
        .addComponent(button3)
        .addComponent(okButton);

Jetzt stöpseln wir die zwei roten Rechteck-Gruppen zusammen:

horizontalGroup.addGroup(labelTextfieldGroup).addGroup(buttonGroup);

Problem 3: Den Buttons die gleiche Breite verpassen.

Dafür gibt’s die Funktion linkSize:

layout.linkSize(SwingConstants.HORIZONTAL,button1,button2,button3,okButton);

SwingConstants.HORIZONTAL gibt an ob man die Breite oder die Höhe verlinken will. Danach kann man beliebig viele Komponenten hinzuschreiben (varargs).

Ein vollständiges Beispiel

An Hand eines Beispiels wollen wir noch die Flexibilität eines GroupLayouts demonstrieren.

Weiterführende Links:

--bERt0r 04:06, 16. Dez 2011 (CET)