Titel | Inhalt | Suchen | Index | API | Go To Java 2, Zweite Auflage, Handbuch der Java-Programmierung |
<< | < | > | >> | Kapitel 31 - GUI-Dialoge |
In vielen grafischen Oberflächen wird die Anordnung der Elemente eines Dialoges durch Angabe absoluter Koordinaten vorgenommen. Dabei wird für jede Komponente manuell oder mit Hilfe eines Ressourcen-Editors pixelgenau festgelegt, an welcher Stelle im Dialog sie zu erscheinen hat.
Da Java-Programme auf vielen unterschiedlichen Plattformen mit unterschiedlichen Ausgabegeräten laufen sollen, war eine solche Vorgehensweise für die Designer des AWT nicht akzeptabel. Sie wählten statt dessen den Umweg über einen Layoutmanager, der für die Anordnung der Dialogelemente verantwortlich ist. Um einen Layoutmanager verwenden zu können, wird dieser dem Fenster vor der Übergabe der Dialogelemente mit der Methode setLayout zugeordnet. Er ordnet dann die per add übergebenen Elemente auf dem Fenster an.
Jeder Layoutmanager implementiert seine eigene Logik bezüglich der optimalen Anordnung der Komponenten:
Neben den gestalterischen Fähigkeiten eines Layoutmanagers bestimmt in der Regel die Reihenfolge der Aufrufe der add-Methode des Fensters die tatsächliche Anordnung der Komponenten auf dem Bildschirm. Wenn nicht - wie es z.B. beim BorderLayout möglich ist - zusätzliche Positionierungsinformationen an das Fenster übergeben werden, ordnet der jeweilige Layoutmanager die Komponenten in der Reihenfolge ihres Eintreffens an.
Um komplexere Layouts realisieren zu können, als die Layoutmanager sie in ihren jeweiligen Grundausprägungen bieten, gibt es die Möglichkeit, Layoutmanager zu schachteln. Auf diese Weise kann auch ohne Vorgabe fester Koordinaten fast jede gewünschte Komponentenanordnung realisiert werden. Sollte auch diese Variante nicht genau genug sein, so bietet sich schließlich durch Verwendung eines Null-Layouts die Möglichkeit an, Komponenten durch Vorgabe fester Koordinaten zu plazieren.
Die Klasse FlowLayout stellt den einfachsten Layoutmanager dar. Alle Elemente werden so lange nacheinander in einer Zeile angeordnet, bis kein Platz mehr vorhanden ist und in der nächsten Zeile fortgefahren wird.
Das FlowLayout
wird einem Fenster durch folgende Anweisung zugeordnet:
setLayout(new FlowLayout());
Neben dem parameterlosen gibt es weitere Konstruktoren, die die Möglichkeit bieten, die Anordnung der Elemente und ihre Abstände voneinander vorzugeben:
public FlowLayout(int align) public FlowLayout(int align, int hgap, int vgap) |
java.awt.FlowLayout |
Der Parameter align gibt an, ob die Elemente einer Zeile linksbündig, rechtsbündig oder zentriert angeordnet werden. Hierzu stehen die Konstanten FlowLayout.CENTER, FlowLayout.LEFT und FlowLayout.RIGHT zur Verfügung, als Voreinstellung wird FlowLayout.CENTER verwendet. Mit hgap und vgap können die horizontalen und vertikalen Abstände zwischen den Komponenten vorgegeben werden. Die Voreinstellung ist 5.
Das folgende Listing zeigt die Verwendung der Klasse FlowLayout am Beispiel von fünf Buttons, die in einem Grafikfenster angezeigt werden. Anstelle der normalen zentrierten Anordnung werden die Elemente linksbündig angeordnet, und der Abstand zwischen den Buttons beträgt 20 Pixel:
001 /* Listing3102.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3102 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3102 wnd = new Listing3102(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3102() 016 { 017 super("Test FlowLayout"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 setLayout(new FlowLayout(FlowLayout.LEFT,20,20)); 021 add(new Button("Button 1")); 022 add(new Button("Button 2")); 023 add(new Button("Button 3")); 024 add(new Button("Button 4")); 025 add(new Button("Button 5")); 026 pack(); 027 } 028 } |
Listing3102.java |
Das Programm erzeugt folgendes Fenster:
Abbildung 31.2: Verwendung der Klasse FlowLayout
Ein GridLayout bietet eine größere Kontrolle über die Anordnung der Elemente als ein FlowLayout. Hier werden die Komponenten nicht einfach nacheinander auf dem Bildschirm positioniert, sondern innerhalb eines rechteckigen Gitters angeordnet, dessen Elemente eine feste Größe haben.
Das Programm übergibt dazu beim Aufruf des Konstruktors zwei Parameter, rows und columns, mit denen die vertikale und horizontale Anzahl an Elementen festgelegt wird:
public void GridLayout(int rows, int columns) |
java.awt.GridLayout |
Beim Aufruf von add werden die Komponenten dann nacheinander in die einzelnen Zellen der Gittermatrix gelegt. Analog zum FlowLayout wird dabei zunächst die erste Zeile von links nach rechts gefüllt, dann die zweite usw.
Ähnlich wie beim FlowLayout steht ein zusätzlicher Konstruktor zur Verfügung, der es erlaubt, die Größe der horizontalen und vertikalen Lücken zu verändern:
public void GridLayout(int rows, int columns, int hgap, int vgap) |
java.awt.GridLayout |
Die größere Kontrolle des Programms über das Layout besteht nun darin, daß der Umbruch in die nächste Zeile genau vorhergesagt werden kann. Anders als beim FlowLayout erfolgt dieser nicht erst dann, wenn keine weiteren Elemente in die Zeile passen, sondern wenn dort so viele Elemente plaziert wurden, wie das Programm vorgegeben hat.
Das folgende Beispiel zeigt ein Fenster mit einem Gitter der Größe 4*2 Elemente, das ingesamt sieben Buttons anzeigt:
001 /* Listing3103.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3103 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3103 wnd = new Listing3103(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3103() 016 { 017 super("Test GridLayout"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 setLayout(new GridLayout(4,2)); 021 add(new Button("Button 1")); 022 add(new Button("Button 2")); 023 add(new Button("Button 3")); 024 add(new Button("Button 4")); 025 add(new Button("Button 5")); 026 add(new Button("Button 6")); 027 add(new Button("Button 7")); 028 pack(); 029 } 030 } |
Listing3103.java |
Die Ausgabe des Programms ist:
Abbildung 31.3: Verwendung der Klasse GridLayout
Wenn wir die Größe des Fensters nicht durch einen Aufruf von pack, sondern manuell festgelegt hätten, wäre an dieser Stelle ein wichtiger Unterschied zwischen den beiden bisher vorgestellten Layoutmanagern deutlich geworden. Abbildung 31.4 zeigt das veränderte Listing 31.2, bei dem die Größe des Fensters durch Aufruf von setSize(500,200); auf 500*200 Pixel festgelegt und der Aufruf von pack entfernt wurde:
Abbildung 31.4: Das FlowLayout in einem größeren Fenster
Hier verhält sich das Programm noch so, wie wir es erwarten würden, denn die Buttons haben ihre Größe behalten, während das Fenster größer geworden ist. Anders sieht es dagegen aus, wenn wir die Fenstergröße in Listing 31.3 ebenfalls auf 500*200 Pixel fixieren:
Abbildung 31.5: Das GridLayout in einem größeren Fenster
Nun werden die Buttons plötzlich sehr viel größer als ursprünglich angezeigt, obwohl sie eigentlich weniger Platz in Anspruch nehmen würden. Der Unterschied besteht darin, daß ein FlowLayout die gewünschte Größe eines Dialogelements verwendet, um seine Ausmaße zu bestimmen. Das GridLayout dagegen ignoriert die gewünschte Größe und dimensioniert die Dialogelemente fest in der Größe eines Gitterelements. Ein LayoutManager hat also offensichtlich die Freiheit zu entscheiden, ob und in welcher Weise er die Größen von Fenster und Dialogelementen den aktuellen Erfordernissen anpaßt.
Wir werden am Ende dieses Abschnitts auch Möglichkeiten kennenlernen, durch Schachteln von Layoutmanagern beide Möglichkeiten zu kombinieren: Das feste Gitterraster bleibt erhalten, aber die einzelnen Komponenten innerhalb des Gitters werden in ihrer gewünschten Größe angezeigt. Am Ende von Kapitel 32 werden wir zusätzlich kurz auf die verschiedenen Größen von Dialogelementen zurückkommen, wenn wir zeigen, wie selbstdefinierte Komponenten erzeugt werden. |
|
Das BorderLayout verfolgt einen anderen Ansatz als die beiden vorigen Layoutmanager, denn die Positionierung der Komponenten wird nicht mehr primär durch die Reihenfolge der Aufrufe von add bestimmt. Statt dessen teilt das BorderLayout den Bildschirm in fünf Bereiche auf, und zwar in die vier Ränder und das Zentrum. Durch Angabe eines Himmelsrichtungs-Strings wird beim Aufruf von add angegeben, auf welchem dieser Bereiche die Komponente plaziert werden soll:
Zur Übergabe dieses Parameters gibt es die Methode add der Klasse Container in einer Variante mit einem String als ersten Parameter:
public void add(String name, Component comp) |
java.awt.Container |
Das folgende Beispiel zeigt die Anordnung von fünf Buttons in einem BorderLayout. Jeweils einer der Buttons wird auf die vier Randbereiche verteilt, und der fünfte Button steht in der Mitte. Die Größe des Fensters wird fest auf 300*200 Pixel eingestellt:
001 /* Listing3104.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3104 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3104 wnd = new Listing3104(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3104() 016 { 017 super("Test BorderLayout"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 setSize(300,200); 021 setLayout(new BorderLayout()); 022 add("North", new Button("Button 1")); 023 add("South", new Button("Button 2")); 024 add("West", new Button("Button 3")); 025 add("East", new Button("Button 4")); 026 add("Center",new Button("Button 5")); 027 } 028 } |
Listing3104.java |
Die Ausgabe des Programms ist:
Abbildung 31.6: Verwendung der Klasse BorderLayout
Bezüglich der Skalierung der Komponenten verfolgt BorderLayout einen Mittelweg zwischen FlowLayout und GridLayout. Während FlowLayout die Komponenten immer in ihrer gewünschten Größe beläßt und GridLayout sie immer skaliert, ist dies bei BorderLayout von verschiedenen Faktoren abhängig:
Auch beim BorderLayout kann die Größe der Lücken zwischen den Elementen an den Konstruktor übergeben werden:
public BorderLayout(int hgap, int vgap) |
java.awt.BorderLayout |
Wenn das vorige Beispiel ein BorderLayout mit einer vertikalen und horizontalen Lücke von 10 Pixeln definieren würde, wäre die Ausgabe des Programms wie folgt:
Abbildung 31.7: Ein BorderLayout mit Lücken
Das GridBagLayout ist der aufwendigste Layoutmanager in Java. Er erlaubt eine sehr flexible Gestaltung der Oberfläche und bietet viele Möglichkeiten, die in den anderen Layoutmanagern nicht zu finden sind. Der Preis dafür ist eine etwas kompliziertere Bedienung und ein höherer Einarbeitungsaufwand. Wir wollen in diesem Abschnitt die Klasse GridBagLayout vorstellen und ihre grundlegenden Anwendungsmöglichkeiten besprechen.
Um mit einem GridBagLayout zu arbeiten, ist wie folgt vorzugehen:
Das könnte beispielsweise so aussehen
001 ... 002 GridBagLayout gbl = new GridBagLayout(); 003 GridBagConstraints gbc = new GridBagConstraints(); 004 setLayout(gbl); 005 List list = new List(); 006 gbc.gridx = 0; 007 gbc.gridy = 0; 008 gbc.gridwidth = 1; 009 gbc.gridheight = 1; 010 gbc.weightx = 100; 011 gbc.weighty = 100; 012 gbc.fill = GridBagConstraints.BOTH; 013 gbl.setConstraints(list, gbc); 014 add(list); 015 ... |
Das Geheimnis der korrekten Verwendung eines GridBagLayout liegt also in der richtigen Konfiguration der Membervariablen der GridBagConstraints-Objekte. Es stehen folgende zur Verfügung:
public int gridx public int gridy public int gridwidth public int gridheight public int anchor public int fill public double weightx public double weighty public Insets insets public int ipadx public int ipady |
java.awt.GridBagConstraints |
Ihre Bedeutung ist:
Um die Anwendung der Parameter zu veranschaulichen, wollen wir uns ein Beispiel ansehen. Das zu erstellende Programm soll ein Fenster mit sechs Dialogelementen entsprechend Abbildung 31.8 aufbauen. Die Liste auf der linken Seite soll beim Skalieren in beide Richtungen vergrößert werden. Die Textfelder sollen dagegen lediglich in der Breite wachsen, ihre ursprüngliche Höhe aber beibehalten. Bei vertikaler Vergrößerung des Fensters sollen sie untereinander stehen bleiben. Die Beschriftungen sollen ihre anfängliche Größe beibehalten und - unabhängig von der Fenstergröße - direkt vor den Textfeldern stehen bleiben. Auch der Button soll seine ursprüngliche Größe beibehalten, wenn das Fenster skaliert wird. Zudem soll er immer in der rechten unteren Ecke stehen. Zwischen den Dialogelementen soll ein Standardabstand von zwei Pixeln eingehalten werden.
Abbildung 31.8: Beispiel für GridBagLayout
Wir konstruieren das GridBagLayout als Zelle der Größe drei mal drei gemäß Abbildung 31.9:
Abbildung 31.9: Zellenschema für GridBagLayout-Beispiel
Die Listbox beginnt also in Zelle (0, 0) und erstreckt sich über eine Zelle in horizontaler und drei Zellen in vertikaler Richtung. Die beiden Labels liegen in Spalte 1 und den Zeilen 0 bzw. 1. Die Textfelder liegen eine Spalte rechts daneben. Textfelder und Beschriftungen sind genau eine Zelle breit und eine Zelle hoch. Der Button liegt in Zelle (2, 2). Die übrigen Eigenschaften werden entsprechend unserer Beschreibung so zugewiesen, daß die vorgegebenen Anforderungen erfüllt werden:
001 /* Listing3106.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3106 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3106 wnd = new Listing3106(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3106() 016 { 017 super("Test GridBagLayout"); 018 setBackground(Color.lightGray); 019 addWindowListener(new WindowClosingAdapter(true)); 020 //Layout setzen und Komponenten hinzufügen 021 GridBagLayout gbl = new GridBagLayout(); 022 GridBagConstraints gbc; 023 setLayout(gbl); 024 025 //List hinzufügen 026 List list = new List(); 027 for (int i = 0; i < 20; ++i) { 028 list.add("This is item " + i); 029 } 030 gbc = makegbc(0, 0, 1, 3); 031 gbc.weightx = 100; 032 gbc.weighty = 100; 033 gbc.fill = GridBagConstraints.BOTH; 034 gbl.setConstraints(list, gbc); 035 add(list); 036 //Zwei Labels und zwei Textfelder 037 for (int i = 0; i < 2; ++i) { 038 //Label 039 gbc = makegbc(1, i, 1, 1); 040 gbc.fill = GridBagConstraints.NONE; 041 Label label = new Label("Label " + (i + 1)); 042 gbl.setConstraints(label, gbc); 043 add(label); 044 //Textfeld 045 gbc = makegbc(2, i, 1, 1); 046 gbc.weightx = 100; 047 gbc.fill = GridBagConstraints.HORIZONTAL; 048 TextField field = new TextField("TextField " + (i +1)); 049 gbl.setConstraints(field, gbc); 050 add(field); 051 } 052 //Ende-Button 053 Button button = new Button("Ende"); 054 gbc = makegbc(2, 2, 0, 0); 055 gbc.fill = GridBagConstraints.NONE; 056 gbc.anchor = GridBagConstraints.SOUTHEAST; 057 gbl.setConstraints(button, gbc); 058 add(button); 059 //Dialogelemente layouten 060 pack(); 061 } 062 063 private GridBagConstraints makegbc( 064 int x, int y, int width, int height) 065 { 066 GridBagConstraints gbc = new GridBagConstraints(); 067 gbc.gridx = x; 068 gbc.gridy = y; 069 gbc.gridwidth = width; 070 gbc.gridheight = height; 071 gbc.insets = new Insets(1, 1, 1, 1); 072 return gbc; 073 } 074 } |
Listing3106.java |
Das Programm besitzt eine kleine Hilfsmethode makegbc, mit der auf einfache Weise ein neues GridBagConstraints-Objekt für einen vorgegebenen Bereich von Zellen erzeugt werden kann. Die übrigen Membervariablen werden im Konstruktor der Fensterklasse zugewiesen. Abbildung 31.10 zeigt das Fenster nach dem Skalieren in x- und y-Richtung.
Abbildung 31.10: Das GridBagLayout-Beispiel nach dem Skalieren
Ein Null-Layout wird erzeugt, indem die Methode setLayout mit dem Argument null aufgerufen wird. In diesem Fall verwendet das Fenster keinen Layoutmanager, sondern überläßt die Positionierung der Komponenten der Anwendung. Für jedes einzufügende Dialogelement sind dann drei Schritte auszuführen:
Der erste und letzte Schritt unterscheiden sich nicht von unseren bisherigen Versuchen, bei denen wir vordefinierte Layoutmanager verwendet haben. Im zweiten Schritt ist allerdings etwas Handarbeit notwendig. Größe und Position des Dialogelements können mit den Methoden move und setSize oder - noch einfacher - mit setBounds festgelegt werden:
public void setBounds( int x, int y, int width, int height ) |
java.awt.Component |
Der Punkt (x,y) bestimmt die neue Anfangsposition des Dialogelements und (width, height) seine Größe.
Das folgende Listing demonstriert diese Vorgehensweise am Beispiel einer treppenartigen Anordnung von fünf Buttons:
001 /* Listing3107.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3107 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3107 wnd = new Listing3107(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3107() 016 { 017 super("Dialogelemente ohne Layoutmanager"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 setSize(300,250); 021 setLayout(null); 022 for (int i = 0; i < 5; ++i) { 023 Button button = new Button("Button"+(i+1)); 024 button.setBounds(10+i*35,40+i*35,100,30); 025 add(button); 026 } 027 } 028 } |
Listing3107.java |
Die Ausgabe des Programms ist:
Abbildung 31.11: Verwendung des Null-Layouts
Eines der Schlüsselkonzepte zur Realisierung komplexer, portabler Dialoge ist die Fähigkeit, Layoutmanager schachteln zu können. Dazu wird an der Stelle, die ein Sublayout erhalten soll, einfach ein Objekt der Klasse Panel eingefügt, das einen eigenen Layoutmanager erhält. Dieses Panel kann mit Dialogelementen bestückt werden, die entsprechend dem zugeordneten Unterlayout formatiert werden.
Wenn wir an die in Abbildung 27.1 vorgestellte Klassenhierarchie innerhalb des AWT denken, werden wir uns daran erinnern, daß ein Panel die einfachste konkrete Containerklasse ist. Sie erbt alle Eigenschaften von Container und bietet damit die Möglichkeit, Komponenten aufzunehmen und mit Hilfe eines Layoutmanagers auf dem Bildschirm anzuordnen.
Das folgende Beispiel zeigt einen Dialog, der ein GridLayout der Größe 1*2 Elemente verwendet. Innerhalb des ersten Elements wird ein Panel mit einem GridLayout der Größe 3*1 Elemente verwendet, innerhalb des zweiten Elements ein BorderLayout:
001 /* Listing3108.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3108 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3108 wnd = new Listing3108(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3108() 016 { 017 super("Geschachtelte Layoutmanager"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 //Panel 1 021 Panel panel1 = new Panel(); 022 panel1.setLayout(new GridLayout(3,1)); 023 panel1.add(new Button("Button1")); 024 panel1.add(new Button("Button2")); 025 panel1.add(new Button("Button3")); 026 //Panel 2 027 Panel panel2 = new Panel(); 028 panel2.setLayout(new BorderLayout()); 029 panel2.add("North", new Button("Button4")); 030 panel2.add("South", new Button("Button5")); 031 panel2.add("West", new Button("Button6")); 032 panel2.add("East", new Button("Button7")); 033 panel2.add("Center", new Button("Button8")); 034 //Hauptfenster 035 setLayout(new GridLayout(1,2)); 036 add(panel1); 037 add(panel2); 038 pack(); 039 } 040 } |
Listing3108.java |
Die Ausgabe des Programms ist:
Abbildung 31.12: Verwendung eines geschachtelten Layouts
Geschachtelte Layouts können auch dann sinnvoll verwendet werden, wenn innerhalb der Bestandteile eines BorderLayouts mehrere Komponenten untergebracht werden sollen oder wenn die Komponenten eines skalierenden Layoutmanagers ihre ursprüngliche Größe beibehalten sollen. |
|
Das folgende Beispiel demonstriert beide Anwendungen anhand eines BorderLayouts, das im South-Element zwei rechtsbündig angeordnete Buttons in Originalgröße und im Center-Element sechs innerhalb eines GridLayouts der Größe 3*2 Elemente angeordnete, skalierte Buttons enthält:
001 /* Listing3109.java */ 002 003 import java.awt.*; 004 import java.awt.event.*; 005 006 public class Listing3109 007 extends Frame 008 { 009 public static void main(String[] args) 010 { 011 Listing3109 wnd = new Listing3109(); 012 wnd.setVisible(true); 013 } 014 015 public Listing3109() 016 { 017 super("Geschachtelte Layoutmanager, Teil II"); 018 addWindowListener(new WindowClosingAdapter(true)); 019 //Layout setzen und Komponenten hinzufügen 020 setSize(300,200); 021 //Panel 1 022 Panel panel1 = new Panel(); 023 panel1.setLayout(new GridLayout(3,2)); 024 panel1.add(new Button("Button1")); 025 panel1.add(new Button("Button2")); 026 panel1.add(new Button("Button3")); 027 panel1.add(new Button("Button4")); 028 panel1.add(new Button("Button5")); 029 panel1.add(new Button("Button6")); 030 //Panel 2 031 Panel panel2 = new Panel(); 032 panel2.setLayout(new FlowLayout(FlowLayout.RIGHT)); 033 panel2.add(new Button("Abbruch")); 034 panel2.add(new Button("OK")); 035 //Hauptfenster 036 setLayout(new BorderLayout()); 037 add("Center",panel1); 038 add("South", panel2); 039 } 040 } |
Listing3109.java |
Die Ausgabe des Programms ist:
Abbildung 31.13: Ein weiteres Beispiel für geschachtelte Layouts
Titel | Inhalt | Suchen | Index | API | Go To Java 2, Zweite Auflage, Addison Wesley, Version 2.0 |
<< | < | > | >> | © 2000 Guido Krüger, http://www.gkrueger.com |