vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 2

Tag 12


Benutzerschnittstellen entwerfen

Der Entwurf einer grafischen Benutzerschnittstelle ist mit dem Malen vergleichbar. Allerdings können Sie momentan nur einer Kunstrichtung nachgehen: dem abstrakten Expressionismus. Sie können Komponenten in eine Benutzerschnittstelle einfügen, allerdings haben Sie nur wenig Kontrolle darüber, wo diese plaziert werden.

Um eine gewisse Form in die Schnittstelle, die Sie mit dem Abstract Windowing Toolkit entwerfen, zu bekommen, müssen Sie eine Reihe von Klassen verwenden, die als Layout-Manager bezeichnet werden.

Heute lernen Sie fünf verschiedene Layout-Manager zu verwenden, um Komponenten auf einer Benutzerschnittstelle anzuordnen. Sie werden die Vorteile der Flexibilität des Abstract Windowing Toolkit von Java nutzen. Dies wurde so entworfen, daß sich die Ergebnisse auf all den verschiedenen Plattformen, die die Sprache unterstützen, präsentiert werden können.

Für den Fall, daß ein Arrangement nicht das erfüllt, was Sie sich für ein Programm vorgestellt haben, lernen Sie, wie Sie verschiedene Layout-Manager auf derselben Schnittstelle zugleich einsetzen.

Lassen Sie uns mit den elementaren Layout-Managern beginnen.

Das elementare Layout einer Benutzerschnittstelle

Wie Sie gestern gelernt haben, ist eine grafische Benutzerschnittstelle, die mit dem AWT entworfen wurde, stets in Fluß. Die Veränderung der Größe eines Fensters kann eine nachhaltige Auswirkung auf Ihre Benutzerschnittstelle haben, da Komponenten an Stellen verschoben werden können, die nicht dem entsprechen, was Sie sich dafür vorgestellt haben.

Dieser Fluß ergibt sich aus einer Notwendigkeit: Java ist auf vielen verschiedenen Plattformen implementiert und zwischen diesen gibt es feine Unterschiede in der Art, wie diese z.B. Schaltflächen, Bildlaufleisten usw. anzeigen.

Bei Programmiersprachen, wie z.B. Microsoft Visual Basic, wird die Position einer Komponente in einem Fenster genau über deren x,y-Koordinaten festgelegt. Einige Java-Entwicklungstools bieten eine ähnliche Kontrolle über eine Benutzerschnittstelle. Dazu verwenden diese spezielle Klassen.

Bei der Verwendung des Abstract Windowing Toolkit erhält ein Programmierer mit Layout-Managern mehr Kontrolle über das Layout der Benutzerschnittstelle.

Das Layout einer Benutzerschnittstelle

Ein Layout-Manager legt fest, wie Komponenten arrangiert werden, sobald diese einem Container hinzugefügt werden.

Der Standard-Layout-Manager ist die Klasse FlowLayout. Diese Klasse läßt Komponenten in der Reihenfolge, in der diese in einen Container eingefügt wurden, von links nach rechts fließen. Sobald kein Platz mehr zur Verfügung steht, wird eine neue Zeile mit Komponenten direkt unter dieser Zeile angefangen - wieder von links nach rechts.

Das AWT beinhaltet fünf elementare Layout-Manager: FlowLayout, GridLayout, BorderLayout , CardLayout und GridBagLayout. Um einen Layout-Manager für einen Container zu erzeugen, wird eine Instanz des Layout-Managers erstellt. Dazu ist eine Anweisung wie die folgende notwendig:

FlowLayout flo = new FlowLayout();

Nachdem Sie einen Layout-Manager erzeugt haben, weisen Sie ihn einem Container über dessen setLayout()-Methode zu. Der Layout-Manager muß festgelegt worden sein, bevor Komponenten dem Container hinzugefügt werden. Wenn kein Layout- Manager festgelegt wurde, wird FlowLayout verwendet.

Die folgenden Anweisungen stellen den Ausgangspunkt für ein Applet dar, das einen Layout-Manager erzeugt und die Methode setLayout() verwendet, so daß dieser Layout-Manager die Anordnung aller Komponenten, die dem Applet-Fenster hinzugefügt werden, kontrolliert:

public class Starter extends java.applet.Applet {
    FlowLayout lm = new FlowLayout();

    public void init() {
        setLayout(lm);
    }
}

Nachdem der Layout-Manager festgelegt wurde, können Sie damit beginnen, Komponenten in den Container, der von diesem Layout-Manager kontrolliert wird, einzufügen. Bei manchen Layout-Managern, wie z.B. FlowLayout, spielt die Reihenfolge, in der die Komponenten hinzugefügt werden, eine wesentliche Rolle. In den einzelnen Abschnitten des heutigen Tages, wenn Sie mit den einzelnen Layout-Managern arbeiten, lernen Sie mehr dazu.

Die Klasse FlowLayout

Die FlowLayout-Klasse stellt das einfachste Layout dar. Mit diesem Layout werden die Komponenten nacheinander zeilenweise in das Panel eingefügt. Paßt eine Komponente nicht in eine Zeile, wird sie automatisch auf die nächste Zeile umbrochen. Das FlowLayout hat eine Ausrichtung, die die Ausrichtung aller Zeilen vorgibt. Standardmäßig ist jede Zeile zentriert.


FlowLayout ordnet Komponenten zeilenweise von links nach rechts an. Die Zeilen werden entweder nach links, rechts oder zentriert ausgerichtet.

Um ein einfaches FlowLayout mit zentrierter Ausrichtung zu erstellen, verwenden Sie in der Panel-Initialisierung folgende Codezeile (da dies das Panel-Standardlayout ist, können Sie, wenn Sie wollen, diese Zeile auslassen):

setLayout(new FlowLayout());

Das Applet in Listing 12.1 zeigt sechs Schaltflächen an, die mit dem FlowLayout angeordnet werden. Da dem FlowLayout-Konstruktor die Klassenvariable FlowLayout.LEFT übergeben wird, werden die einzelnen Komponenten an der linken Seite des Applets ausgerichtet.

Listing 12.1: Der gesamte Quelltext von Alphabet.java

 1: import java.awt.*;
 2:
 3: public class Alphabet extends java.applet.Applet {
 4:     Button a = new Button("Alibi");
 5:     Button b = new Button("Burglar");
 6:     Button c = new Button("Corpse");
 7:     Button d = new Button("Deadbeat");
 8:     Button e = new Button("Evidence");
 9:     Button f = new Button("Fugitive");
10:     FlowLayout lm = new FlowLayout(FlowLayout.LEFT);
11:
12:     public void init() {
13:         setLayout(lm);
14:         add(a);
15:         add(b);
16:         add(c);
17:         add(d);
18:         add(e);
19:         add(f);
20:     }
21: }

Das folgende <APPLET>-Tag wurde verwendet, um das Applet im Appletviewer, wie in Abbildung 12.1 dargestellt, anzuzeigen.

<applet code="Alphabet.class" height=120 width=220>
</applet>


Abbildung 12.1:
Sechs mit einem FlowLayout-Manager angeordnete Schaltflächen

Um ein FlowLayout mit einer rechten oder linken Ausrichtung zu erstellen, fügen Sie die Klassenvariable FlowLayout.RIGHT oder FlowLayout.LEFT als Argument ein:

setLayout(new FlowLayout(FlowLayout.LEFT));

Mit Hilfe des FlowLayout können Sie auch horizontale und vertikale Abstandswerte setzen. Der Abstand ist die Zahl der Pixel zwischen Komponenten in einem Panel. Standardmäßig sind die horizontalen und vertikalen Abstandswerte drei Pixel, was sehr eng ist. Der horizontale Abstand ist links und rechts zwischen Komponenten, der vertikale oben und unten. Um den Abstand zu erhöhen, fügen Sie in den FlowLayout- Konstruktor ganzzahlige Argumente ein. Sie können einen Abstand von 30 Punkten in horizontaler und 10 in vertikaler Ausrichtung wie folgt einfügen:

setLayout(new FlowLayout(FlowLayout.LEFT, 30, 10));

Die Klasse GridLayout

Layouts mit GridLayout bieten mehr Kontrolle über die Anordnung von Komponenten in einem Panel. Mit einem GridLayout können Sie den Panel-Bereich in Zeilen und Spalten aufteilen. Jede Komponente, die Sie dann in das Panel einfügen, wird in einer Zelle des Rasters von links oben nach rechts unten in das Raster eingefügt (hier ist die Reihenfolge, in der die add()-Methode aufgerufen wird, für das Bildschirmlayout relevant).

Um ein GridLayout zu erstellen, geben Sie die Anzahl der gewünschten Zeilen und Spalten in einer neuen Instanz der GridLayout-Klasse an. Hier ein GridLayout mit drei Zeilen und zwei Spalten. In das Raster werden sechs Schaltflächen eingefügt.

import java.awt.*;

public class GridLayoutTest extends java.applet.Applet {

   public void init() {
      setLayout(new GridLayout(3,2);
      add(new Button("One"));
      add(new Button("Two"));
      add(new Button("Three"));
      add(new Button("Four"));
      add(new Button("Five"));
      add(new Button("Six"));
   }
}

Auch bei einem GridLayout können Sie den horizontalen und vertikalen Abstand zwischen den Komponenten bestimmen. Hierfür fügen Sie die entsprechenden Pixel- Werte ein:

setLayout(new GridLayout(3, 3, 10, 30));

Der Standardabstand zwischen einzelnen Komponenten beträgt sowohl horizontal als auch vertikal null Pixel.

Listing 12.2 beinhaltet ein Applet, das ein Raster mit 3 Zeilen, 3 Spalten und einem vertikalen und horizontalen Abstand von 10 Pixeln zwischen den einzelnen Komponenten erzeugt.

Listing 12.2: Der gesamte Quelltext von Bunch.java

 1: import java.awt.*;
 2:
 3: public class Bunch extends java.applet.Applet {
 4:     GridLayout family = new GridLayout(3,3,10,10);
 5:     Button marcia = new Button("Marcia");
 6:     Button carol = new Button("Carol");
 7:     Button greg = new Button("Greg");
 8:     Button jan = new Button("Jan");
 9:     Button alice = new Button("Alice");
10:     Button peter = new Button("Peter");
11:     Button cindy = new Button("Cindy");
12:     Button mike = new Button("Mike");
13:     Button bobby = new Button("Bobby");
14:
15:     public void init() {
16:         setLayout(family);
17:         add(marcia);
18:         add(carol);
19:         add(greg);
20:         add(jan);
21:         add(alice);
22:         add(peter);
23:         add(cindy);
24:         add(mike);
25:         add(bobby);
26:     }
27: }

Abbildung 12.2 zeigt dieses Applet auf einer Seite mit dem folgenden <APPLET>-Tag:

<applet code="Bunch.class" height=160 width=160>
</applet>


Abbildung 12.2:
Neun Schaltflächen, die in einem 3× 3-Raster angeordnet wurden

Beachten Sie bei den Schaltflächen in Abbildung 12.2 bitte, das diese so vergrößert werden, daß sie den gesamten verfügbaren Raum in der jeweiligen Zelle einnehmen. Dies ist ein wesentlicher Unterschied zwischen dem GridLayout und anderen Layout- Managern. Beim GridLayout nehmen die einzelnen Komponenten immer den gesamten Raum einer Zelle ein. Wenn Sie das Bunch-Applet mit dem Appletviewer laden, dann werden Sie feststellen, daß sich die Größe der Schaltflächen ändert, sobald Sie die Größe des Applet-Fensters ändern.

Die Klasse BorderLayout

Layouts mit BorderLayout verhalten sich anders als FlowLayout und GridLayout. Wenn Sie eine Komponente in ein Panel einfügen, das auf einem BorderLayout basiert, müssen Sie seine Anordnung als geographische Richtung angeben: Nord, Süd, Ost, West oder Mitte. (Siehe Abbildung 12.3.) Die Komponenten rund um die Kanten werden in der benötigten Größe ausgelegt. Falls es eine Komponente in der Mitte gibt, erhält sie den restlichen Platz zugeteilt.


Abbildung 12.3:
Anordnung der Komponenten in einem BorderLayout

Um ein BorderLayout zu erstellen, verfahren Sie wie bei den anderen Layouts; fügen Sie dann die einzelnen Komponenten mit einer speziellen, zwei Argumente beinhaltenden add()-Methode ein. Das erste Argument ist eine Zeichenkette, die die Position der Komponente im Layout bezeichnet, und das zweite die einzufügende Komponente:

add("North", new TextField("Title", 50));

Sie können diese Form von add() auch für andere Layout-Manager verwenden; das Argument für die Zeichenkette wird einfach ignoriert, falls es nicht benötigt wird.

Mit dem Code aus Listing 12.3 erstellen Sie das in Abbildung 12.3 gezeigte BorderLayout :

Listing 12.3: Der gesamte Quelltext von Border.java

 1: import java.awt.*;
 2:
 3: public class Border extends java.applet.Applet {
 4:     BorderLayout b = new BorderLayout();
 5:     Button north = new Button("North");
 6:     Button south = new Button("South");
 7:     Button east = new Button("East");
 8:     Button west = new Button("West");
 9:     Button center = new Button("Center");
10:
11:     public void init() {
12:         setLayout(b);
13:         add("North", north);
14:         add("South", south);
15:         add("East", east);
16:         add("West", west);
17:         add("Center", center);
18:     }
19: }

Zusätzlich wurde das folgende <APPLET>-Tag verwendet:

<applet code="Border.class" height=120 width=120>
</applet>

Auch bei BorderLayout sind horizontale und vertikale Abstände möglich. Beachten Sie, daß sich die Nord- und Südkomponenten über die gesamte Kante des Panels erstrecken, so daß der Abstand auf Kosten des Platzes für die Komponenten in Ost, West und Mitte entsteht. Um Abstände in ein BorderLayout einzufügen, tragen Sie, wie bei den anderen Layout-Managern, die Pixel-Werte in den Konstruktor ein:

setLayout(new BorderLayout(10, 10));

Verschiedene Layout-Manager gleichzeitig

An diesem Punkt werden Sie sich vielleicht fragen, wie Sie mit den Layout-Managern des Abstract Windowing Toolkit die Benutzerschnittstelle, die Sie sich vorgestellt haben, entwerfen sollen. Die einzelnen Layout-Manager für sich genommen haben wahrscheinlich nicht genau die Eigenschaften, die Sie benötigen.

Um das Layout zu finden, das genau richtig ist, müssen Sie oftmals mehr als einen Layout-Manager in derselben Benutzerschnittstelle kombinieren.

Dies erreichen Sie, indem Sie in den Haupt-Container (z.B. ein Applet-Fenster) weitere Container einfügen. Jedem dieser Unter-Container weisen Sie einen eigenen Layout-Manager zu.

Diese Unter-Container sind Panels - Objekte der Klasse Panel. Panels sind Container, die zur Gruppierung von Komponenten verwendet werden. Zwei Dinge sollten Sie sich in bezug auf den Umgang mit Panels merken:

Panels werden mit einem einfachen Aufruf des Konstruktors der Panel-Klasse erzeugt, wie das in dem folgenden Beispiel gezeigt wird:

Panel pane = new Panel();

Der Layout-Manager für das Panel wird über einen Aufruf der Methode setLayout() des Panels festgelegt. Diese funktioniert genauso, wie der Aufruf der setLayout()-Methode, die Sie zuvor für das Applet-Fenster verwendet haben - sowohl Applet als auch Panel sind Subklassen der Klasse Container, und sie erben das Verhalten für das Layout-Management von deren Superklasse.

Die folgenden Anweisungen erzeugen einen Layout-Manager und weisen diesen einem Panel-Objekt mit dem Namen pane zu:

BorderLayout bo = new BorderLayout();
pane.setLayout(bo);

Komponenten werden dem Panel über einen Aufruf der add()-Methode hinzugefügt. Dies funktioniert hier genauso wie bei anderen Containern, wie z.B. Applets.

Die folgende Anweisung fügt einen Textbereich mit dem Namen dialogue in das Panel -Objekt pane ein:

pane.add(dialogue);

Sie werden in den weiteren Beispielprogrammen des heutigen Tages noch viele Beispiele für die Verwendung von Panels sehen.

Fortgeschrittene Layout-Manager

Zusätzlich zu den drei Layout-Managern, die Sie bereits kennengelernt haben - FlowLayout , GridLayout und BorderLayout -, beinhaltet das Abstract Windowing Toolkit zwei weitere, ausgefeiltere Layout-Manager. Die beiden Layout-Manager CardLayout und GridBagLayout können auch mit anderen Layout-Managern zusammen verwendet werden, indem man Container ineinander verschachtelt.

Die Klasse CardLayout

Layouts mit CardLayout unterscheiden sich von anderen Layouts. Wenn Sie in einen der anderen Layout-Manager Komponenten einfügen, erscheinen diese Komponenten sofort am Bildschirm. CardLayout wird dazu verwendet, um eine Art Diaschau der Komponenten zu erzeugen. Falls Sie je mit HyperCard auf dem Macintosh gearbeitet haben, kennen Sie das Prinzip von CardLayout.

Die Komponenten, die Sie beim Erstellen eines CardLayout in das äußere Panel einfügen, werden im allgemeinen als weitere Container-Komponenten - normalerweise sind es Panels - behandelt. Sie können dann für die einzelnen Karten je ein anderes Layout verwenden, so daß jeder Bildschirm anders aussieht.


Cards (Karten) in einem CardLayout sind unterschiedliche Panels, die nacheinander eingefügt und angezeigt werden. Wenn Sie sich ein Kartenspiel vorstellen, werden Sie verstehen, was gemeint ist; es kann jeweils nur eine Karte angezeigt werden, aber Sie können zwischen den Karten wechseln.

Jede Karte, die Sie in das Panel einfügen, wird benannt. Um jeweils zwischen den Container-Karten zu blättern, können Sie in der CardLayout-Klasse definierte Methoden verwenden, mit denen Sie zu einer benannten Karte gelangen, vorwärts oder rückwärts, oder zur ersten oder letzten Karte blättern. Natürlich steht Ihnen zum Aufrufen dieser Methoden eine Gruppe von Schaltflächen zur Verfügung, die Ihnen das Navigieren durch das CardLayout erleichtern.

Mit den folgenden zwei einfachen Anweisungen wird ein CardLayout mit drei Karten erstellt:

setLayout(new CardLayout());
//Karten hinzufügen 
Panel one = new Panel()
add("first", one);
Panel two = new Panel()
add("second", two);
Panel three = new Panel()
add("third", three);

//navigieren 
show(this, "second");  //Sprung zu Karte namens "second"
show(this, "third");   //Sprung zu Karte namens "third"
previous(this);        //Zurück zur zweiten Karte 
first(this);           //Sprung zur ersten Karte

In einem Programm, das das CardLayout verwendet, wird der Wechsel zwischen den Karten normalerweise durch eine Aktion des Benutzers ausgelöst. In einem Programm, das Adressen auf verschiedenen Karten anzeigt, könnte der Benutzer eine Karte für die Anzeige auswählen, indem er einen Eintrag in einer Liste selektiert. Als Alternative dazu verwendet das Applet in Listing 12.4 eine Animation in einem Thread, um zwischen den einzelnen Panels in dem CardLayout umzuschalten.

Listing 12.4: Der gesamte Quelltext von BurmaShave.java

 1: import java.awt.*;
 2:
 3: public class BurmaShave extends java.applet.Applet
 4:     implements Runnable {
 5:
 6:     CardLayout card = new CardLayout();
 7:     Label[] lab = new Label[6];
 8:     int current = 0;
 9:     Thread runner;
10:
11:     public void start() {
12:         if (runner == null) {
13:             runner = new Thread(this);
14:             runner.start();
15:         }
16:     }
17:
18:     public void stop() {
19:         runner = null;
20:     }
21:
22:     public void init() {
23:         lab[0] = new Label("Grandpa's beard");
24:         lab[1] = new Label("Was stiff and coarse.");
25:         lab[2] = new Label("And that's what caused");
26:         lab[3] = new Label("His fifth");
27:         lab[4] = new Label("Divorce.");
28:         lab[5] = new Label("Burma Shave.");
29:         setLayout(card);
30:         for (int i = 0; i < 6; i++)
31:             add("Card " + i, lab[i]);
32:     }
33:
34:     public void run() {
35:         Thread thisThread = Thread.currentThread();
36:         while (runner == thisThread) {
37:             card.show(this, "Card " + current);
38:             current++;
39:             if (current > 5)
40:                 current = 0;
41:             repaint();
42:             try {
43:                 Thread.sleep(5000);
44:             } catch (InterruptedException e) { }
45:         }
46:     }
47: }

Das folgende <APPLET>-Tag wurde verwendet, um die Ausgabe in Abbildung 12.4 zu erzeugen.

<applet code="BurmaShave.class" height=80 width=160>
</applet>


Abbildung 12.4:
Eine Karte aus einem CardLayout mit mehreren Karten

Das BurmaShave-Applet verwendet ein CardLayout mit sechs Karten. Jede Karte ist eine Label-Komponente. Die Animation wird über den Wechsel zwischen den einzelnen Karten erzeugt.

Einige Anmerkungen zu dem Applet:

Die Klasse GridBagLayout

Das GridBagLayout haben wir uns bis zum Schluß aufgehoben. Es ist zwar das stärkste Instrument in der Verwaltung von AWT-Layouts, jedoch ist es gleichzeitig auch äußerst kompliziert.

Genau das gewünschte Layout zu erhalten, kann sich bei Verwendung einer der vier anderen Layout-Manager manchmal recht schwierig gestalten, ohne viele Panels ineinander verschachteln zu müssen. Das GridLayout bietet eine allgemeineren Zwekken dienliche Lösung. Wie das GridLayout ermöglicht Ihnen das GridBagLayout die Anordnung Ihrer Komponenten in einem rasterähnlichen Layout. Allerdings bietet Ihnen das GridBagLayout zusätzlich die Möglichkeit, die Weite einzelner Zellen im Raster, die Proportionen zwischen Zeilen und Spalten sowie die Anordnung von Komponenten innerhalb der Zellen im Raster zu kontrollieren.

Zur Erstellung eines GridBagLayout benutzen Sie zwei Klassen: GridBagLayout, die den gesamten Layout-Manager bereitstellt, und die Klasse GridBagConstraints, die die Eigenschaften jeder Komponente im Raster bestimmt - seine Anordnung, Maße, Ausrichtung usw. Das Verhältnis zwischen GridBagLayout, GridBagConstraints und jeder Komponente bestimmt das gesamte Layout.

Zur Erstellung eines GridBagLayout in seiner einfachsten Form gehen Sie folgendermaßen vor:

1. Erstellen Sie ein GridBagLayout-Objekt, und definieren Sie es in gleicher Weise wie andere Layout-Manager als den derzeitigen Layout-Manager.

2. Erstellen Sie eine neue Instanz von GridBagConstraints.

3. Richten Sie die Rahmenbedingungen für eine Komponente ein.

4. Informieren Sie den Layout-Manager über die Komponente und Ihre GridBagConstraints .

5. Fügen Sie die Komponente in das Panel ein.

Der folgende einfache Code erstellt das Layout und erzeugt dann GridBagConstraints für eine einzelne Schaltfläche. (Machen Sie sich über die verschiedenen Werte der GridBagConstraints keine Sorgen; darüber reden wir später in diesem Abschnitt.)

// Layout einrichten 
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
setLayout(gridbag);

// Constraints für die Schaltfläche definieren 
Button b = new Button("Save");
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1; 
constraints.weightx = 30;
constraints.weighty = 30;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.CENTER;

// Constraints zu Layout und Button hinzufügen 
gridbag.setConstraints(b, constraints);
add(b);

Wie Sie anhand des Beispiels sehen können, müssen Sie alle GridBagConstraints für jede Komponente, die Sie in das Panel einfügen wollen, setzen. Hinzu kommt, daß GridBagConstraints wirklich nicht so einfach sind; sie haben viele verschiedene Werte, von denen viele zueinander in Beziehung stehen, was bedeutet, daß die Änderung eines Werts direkte Auswirkungen auf andere haben kann. Bei den zahlreichen GridBagConstraints ist es hilfreich, planmäßig vorzugehen und sich mit jeder GridBagConstraints -Eigenschaft einzeln zu beschäftigen.

Erster Schritt: Rasterentwurf

Der erste Schritt zur Erstellung eines GridBagLayout besteht in einem Entwurf auf Papier. Die Erstellung des Designs Ihrer Benutzeroberfläche in Form einer Skizze vorab - noch bevor Sie auch nur eine Codezeile schreiben - wird auf lange Sicht gesehen enorm hilfreich sein, wenn Sie versuchen herauszufinden, wo was hinkommt. Wenden Sie sich also einmal von Ihrem Editor ab, nehmen Sie »Papier und Bleistift«, und entwerfen Sie das Raster.

Abbildung 12.5 zeigt das Panel-Layout, das Sie in diesem Beispiel konstruieren. Abbildung 12.6 zeigt dasselbe Layout mit einem aufgesetzten Raster. Ihr Layout wird ein ähnliches Raster haben, in dem Zeilen und Spalten einzelne Zellen bilden.


Abbildung 12.5:
Ein GridBagLayout


Abbildung 12.6:
Das GridBagLayout aus Abbildung 12.5 mit darüberliegendem Raster

Denken Sie beim Zeichnen Ihres Rasters daran, daß jede Komponente ihre eigene Zelle haben muß. Nur jeweils eine Komponente kann in dieselbe Zelle gesetzt werden. Umgekehrt stimmt das nicht, allerdings kann eine Komponente mehrere Zellen in x- oder y-Richtung umfassen (wie bei der Schaltfläche OK in der unteren Zeile, die zwei Spalten umfaßt). Beachten Sie, daß die Labels und Textfelder in Abbildung 12.6 ihre eigenen Raster haben und die Schaltfläche horizontal zwei Zellen umfaßt.

Setzen Sie vorerst Ihre Arbeit auf dem Papier fort, und beschriften Sie die Zellen mit ihren x- und y-Koordinaten. Sie werden später sehen, wie nützlich das ist. Es handelt sich nicht um Pixelkoordinaten, sondern um Feldkoordinaten. Die Zelle links oben ist 0,0. Die nächste Zelle rechts davon in der oberen Zeile ist 1,0. Die Zelle rechts von dieser ist 2,0. In der nächsten Zeile ist die Zelle ganz links 1,0, die nächste Zelle in dieser Zeile ist 1,1 usw. Beschriften Sie Ihre Zellen auf dem Papier mit diesen Nummern; Sie werden sie später benötigen, wenn Sie den Code für dieses Beispiel erstellen. In Abbildung 12.7 sind die Zahlen für jede Zelle des Beispiels angezeigt.


Abbildung 12.7:
Das GridBagLayout gemäß Abbildung 12.14 mit Feldkoordinaten

Zweiter Schritt: Rastererstellung in Java

Wenden Sie sich jetzt wieder Java zu, und beginnen Sie mit der Realisierung des auf dem Papier vorbereiteten Layouts. Wir konzentrieren uns zuerst ausschließlich auf das Layout - damit das Raster und die Proportionen stimmen. Dabei ist es hilfreich, nicht mit den Elementen der Benutzeroberfläche zu arbeiten. Schaltflächen dienen als Platzhalter für die Elemente im Layout, bis alles richtig eingerichtet ist. Danach werden die Schaltflächen in richtige Elemente umgewandelt.

Um Ihr Pensum an Eigenarbeit zum Einrichten all dieser GridBagConstraints-Eigenschaften zu reduzieren, können Sie mit der Definition einer Hilfsmethode beginnen, die mehrere Werte entgegennimmt und die Rahmenbedingungen für diese Werte setzt. buildConstraints() erhält dazu sieben Argumente: ein GridBagConstraints- Objekt und sechs Ganzzahlen, die für die GridBagConstraints-Instanzvariablen gridx , gridy, gridwidth, gridheight, weightx und weighty stehen. Was deren Funktion ist, werden Sie in Kürze lernen; im Moment beschäftigen wir uns mit dem Code der Hilfsmethode, den Sie im weiteren Verlauf dieses Beispiels einsetzen:

void buildConstraints(GridBagConstraints gbc, int gx, int gy,
int gw, int gh, int wx, int wy) {
gbc.gridx = gx;
gbc.gridy = gy;
gbc.gridwidth = gw;
gbc.gridheight = gh;
gbc.weightx = wx;
gbc.weighty = wy;
}

Wenden wir uns jetzt der init()-Methode zu. In dieser wird das gesamte Layout aufgebaut. Es folgt die grundlegende Definition der Methode, in der Sie das GridBagLayout als Layout-Manager definieren und ein GridBagConstraints-Objekt erzeugen:

public void init() {
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
setLayout(gridbag);

constraints.fill = GridBagConstraints.BOTH;
}

Noch ein kurzer erklärender Hinweis: Die letzte Zeile, die den Wert für constraints.fill setzt, wird später entfernt (und erklärt). Sie dient dem Zweck, daß die Komponenten die gesamte, sie beinhaltende Zelle füllen, was Ihnen die Möglichkeit gibt, zu sehen, was vor sich geht. Fügen Sie sie jetzt hinzu, und Sie werden ihren späteren Zweck verstehen.

Fügen Sie jetzt die Platzhalter-Schaltflächen in das Layout ein (denken Sie daran, daß Sie sich momentan auf einfache Rasterorganisation konzentrieren und deshalb Schaltflächen als Platzhalter für echte, später hinzuzufügende Elemente der Benutzeroberfläche verwenden). Beginnen Sie mit einer einzelnen Schaltfläche, damit Sie ein Gefühl für das Setzen der GridBagConstraints entwickeln. Dieser Code kommt in die init() -Methode direkt im Anschluß an die Zeile setLayout():

//Label für das Namensfeld
buildConstraints(constraints, 0, 0, 1, 1, 100, 100);
Button label1 = new Button("Name:");
gridbag.setConstraints(label1, constraints);
add(label1);

Diese vier Zeilen richten die GridBagConstraints für ein Objekt ein, erstellen eine neue Schaltfläche, weisen der Schaltfläche die GridBagConstraints zu und fügen sie dann in das Panel ein. Beachten Sie, daß die Rahmenbedingungen (deutsch für Constraints) für eine Komponente in einem GridBagConstraints-Objekt gespeichert werden, was bedeutet, daß die Komponente nicht einmal vorhanden sein muß, um ihre Rahmenbedingungen einzurichten.

Nun können Sie sich den Einzelheiten widmen: Welches sind die Werte für die Rahmenbedingungen, die Sie in die Hilfsmethode buildConstraints() eingebunden haben?

Die ersten beiden ganzzahligen Argumente sind die Werte gridx und gridy der Rahmenbedingungen. Sie stellen die Feldkoordinaten der Zelle dar, die diese Komponente aufnehmen soll. Erinnern Sie sich, daß Sie diese Koordinaten in Schritt 1 in Ihre Skizze geschrieben haben? Da Sie die Zellen sozusagen schon mit Nummern auf dem Papier versehen haben, müssen Sie jetzt nur noch die richtigen Werte einsetzen. Beachten Sie bei einer mehrere Zellen umfassenden Komponente, daß die Koordinaten der Zelle sich auf die Zelle in der obersten linken Ecke beziehen.

Hier ist diese Schaltfläche in der obersten linken Ecke, und somit sind ihre Werte für gridx und gridy (die ersten beiden Argumente für buildConstraints()) 0 bzw. 0.

Die zweiten zwei ganzzahligen Argumente sind gridwidth und gridheight. Sie stellen keine Pixelbreiten und -höhen der Zelle dar, sondern die Anzahl der Zellen, die diese Komponente umfaßt: gridwidth für die Spalten und gridheight für die Zeilen. In unserem Beispiel umfaßt diese Komponente nur eine Zelle, und somit ist der Wert für beide 1.

Die letzten beiden ganzzahligen Argumente stehen für weightx und weighty. Sie dienen zum Einrichten der Proportionen der Zeilen und Spalten - bzw. zur Angabe von deren Breite und Tiefe. Die Weights-Variablen können sehr verwirrend sein, deshalb setzen Sie beide Werte jetzt einfach auf 100. Mit Weights werden wir uns in Schritt 3 beschäftigen.

Nachdem Sie die Rahmenbedingungen erstellt haben, können Sie sie mit der Methode setConstraints() an ein Objekt anbinden. Diese Methode wird in GridBagLayout definiert und arbeitet mit zwei Argumenten: der Komponente (hier eine Schaltfläche) und den Rahmenbedingungen für diese Schaltfläche. Zum Schluß können Sie die Schaltfläche in das Panel einfügen.

Wenn Sie die Rahmenbedingungen gesetzt und einer Komponente zugeordnet haben, können Sie das Objekt GridBagConstraints zur Einrichtung der Rahmenbedingungen für das nächste Objekt erneut verwenden. Hierfür duplizieren Sie diese vier Zeilen für jede Komponente im Raster, wobei Sie unterschiedliche Werte für die Methode buildConstraints() verwenden. Um Platz zu sparen, werden Ihnen nur die buildConstraints() -Methoden für die letzten vier Zellen gezeigt.

Die zweite einzufügende Zelle ist die das Textfeld für den Namen beinhaltende Zelle. Die Koordinaten für diese Zelle sind 1,0 (zweite Spalte, erste Zeile); auch in diesem Fall wird nur eine Zelle eingefaßt, und die Weights sind (für den Moment) auch jeweils 100:

buildConstraints(constraints, 1, 0, 1, 1, 100, 100);

Die nächsten beiden Komponenten, ein Label und ein Textfeld, sind mit den beiden vorherigen beinahe identisch; den einzigen Unterschied bilden die Koordinaten der Zelle. Das Paßwort-Label ist auf 0,1 (erste Spalte, zweite Zeile) und das Paßworttextfeld auf 1,1 (zweite Spalte, zweite Zeile) gesetzt:

buildConstraints(constraints, 0, 1, 1, 1, 100, 100);
buildConstraints(constraints, 1, 1, 1, 1, 100, 100);

Zum Schluß benötigen Sie die Schaltfläche OK, welche eine zwei Zellen auf der unteren Zeile des Panels umfassende Komponente ist. In diesem Fall sind die Zellkoordinaten die Koordinaten der Zelle ganz links oben des mehrzelligen Bereichs, den die Komponente einnimmt (0,2). Im Gegensatz zu den vorherigen Komponenten setzen Sie hier gridwidth auf einen von 1 verschiedenen Wert, da diese Zelle mehrere Spalten umfassen kann. gridweight ist 2 (sie umfaßt zwei Spalten), und gridheight ist 1 (sie umfaßt nur eine Zeile):

buildConstraints(constraints, 0, 2, 2, 1, 100, 100);

Haben Sie es verstanden? Nun haben Sie die Anordnung der Rahmenbedingungen für alle Komponenten, die Sie in das GridBagLayout einfügen möchten, gesetzt. Sie müssen allerdings auch die Rahmenbedingungen jeder Komponente dem Layout-Manager zuordnen und dann jede Komponente in das Panel einfügen. Abbildung 12.8 zeigt das bisherige Ergebnis. Beachten Sie, daß Sie sich hier keine Gedanken um die genauen proportionalen Verhältnisse machen müssen, oder darum, sicherzustellen, daß alles richtig arrangiert ist. Was Sie jetzt im Auge behalten sollten, ist sicherzustellen, daß das Raster funktioniert, daß Sie die richtige Anzahl von Zeilen und Spalten angegeben haben, daß die Spannweiten korrekt sind und daß nichts Auffälliges (Zellen am falschen Platz, sich überlappende Zellen oder ähnliches) zu erkennen ist.


Abbildung 12.8:
GridBagLayout - erster Arbeitsgang

Dritter Schritt: Festlegen der Proportionen

Im nächsten Schritt geht es um die Festlegung der Proportionen von Zeilen und Spalten im Verhältnis zu anderen Zeilen und Spalten. Nehmen wir an, Sie möchten beispielsweise den Platz für die Labels (Name und Paßwort) kleiner als für die Textfelder gestalten. Außerdem möchten Sie die Höhe der Schaltfläche OK im unteren Teil auf lediglich die halbe Höhe der beiden darüber angebrachten Textfelder reduzieren. Verwenden Sie die Rahmenbedingungen weightx und weighty zur Anordnung der Proportionen der Zelle in Ihrem Layout.

Der einfachste Weg, mit weightx und weighty umzugehen, ist es, sich ihre Werte als Prozentsätze der Gesamtbreite und -höhe des Panels vorzustellen. Diese können entweder 0 oder eine andere Zahl sein, falls die Gewichtung oder Höhe von einer anderen Zelle gesetzt wurde. Deshalb sollten die Werte für weightx und weighty aller Komponenten eine Gesamtsumme von 100 ergeben.


Eigentlich sind die Werte für weightx und weighty keine Prozentsätze; es sind einfach Proportionen - und können einen beliebigen Wert annehmen. Bei der Berechnung der Proportionen werden alle Werte in eine Richtung aufsummiert, so daß jeder einzelne Wert im proportionalen Verhältnis zu dieser Gesamtsumme steht (anders gesagt, durch die Gesamtsumme geteilt, um tatsächlich einen Prozentsatz zu erhalten). Da dieses ganze Verfahren nicht intuitiv ist, ist es wesentlich einfacher, das ganze als Prozentsätze zu betrachten und sicherzustellen, daß das Ergebnis ihrer Summe 100 ist, womit wir auf der sicheren Seite sind.

Welche Zellen erhalten also Werte und welche erhalten 0? Mehrere Zeilen und Spalten umfassende Zellen sollten immer 0 sein, in der Richtung, in der die Komponente sich über mehrere Zellen erstreckt. Darüber hinaus liegt die Entscheidung einfach in der Wahl eines Werts für eine Zelle, wonach dann alle anderen Zellen in dieser Zeile oder Spalte 0 sein sollten.

Schauen wir uns einmal die fünf Aufrufe von buildConstraints(), die im vorhergehenden Schritt ausgeführt wurden, an:

buildConstraints(constraints, 0, 0, 1, 1, 100, 100); //Label für Namensfeld
buildConstraints(constraints, 1, 0, 1, 1, 100, 100); //Textfeld für den Namen
buildConstraints(constraints, 0, 1, 1, 1, 100, 100); //Label für Paßwort-Feld
buildConstraints(constraints, 1, 1, 1, 1, 100, 100); //Textfeld für das Paßwort
buildConstraints(constraints, 0, 2, 2, 1, 100, 100); //OK-Schaltfläche

Die letzten beiden Argumente müssen Sie bei jedem Aufruf von buildConstraints() entweder in einen Wert oder 0 umändern. Beginnen wir mit der x-Richtung (die Proportionen der Spalten), welche das zweitletzte Argument in dieser Liste ist.

Wenn Sie sich Abbildung 12.6 noch einmal anschauen (die Illustration des Panels mit dem aufgesetzten Raster), werden Sie feststellen, daß der Umfang der zweiten Spalte bei weitem größer als der der ersten ist. Nehmen wir an, Sie wollen die theoretischen Prozentsätze für diese Spalten auswählen, und nehmen wir weiter an, daß Sie den ersten Prozentsatz mit 10 und den zweiten mit 90 festlegen (alles rein theoretische Annahmen - so sollten auch Sie vorgehen). Diese beiden angenommenen Prozentsätze können Sie nun Zellen zuordnen. Sie können der Zelle nicht beliebige Werte mit der Schaltfläche OK zuordnen, weil die Zelle beide Spalten einfaßt und deshalb hier nicht mit Prozentsätzen gearbeitet werden kann. Fügen Sie also den ersten beiden Zellen das Label für das Namensfeld und das Textfeld für den Namen hinzu:

buildConstraints(constraints, 0, 0, 1, 1, 10, 100); //Label für das Namensfeld
buildConstraints(constraints, 1, 0, 1, 1, 90, 100); //Textfeld für den Namen

Und was ist mit den Werten der verbleibenden zwei Zellen, dem Label für das Paßwort-Feld und dem Textfeld dazu? Da mit dem Label für das Namensfeld und dem Textfeld für den Namen die Proportionen der Spalten bereits festgelegt wurden, müssen sie hier nicht neu gesetzt werden. Geben Sie diesen beiden und der Zelle für das OK-Feld die Werte 0:

buildConstraints(constraints, 0, 1, 1, 1, 0, 100); //Label für das Paßwort-Feld
buildConstraints(constraints, 1, 1, 1, 1, 0, 100); //Textfeld für das Paßwort
buildConstraints(constraints, 0, 2, 2, 1, 0, 100); //OK-Schaltfläche

Beachten Sie in diesem Fall, das der Wert 0 nicht bedeutet, das die Zellenbreite 0 ist. Bei diesen Werten handelt es sich um Proportionen, nicht Pixel-Werte. Eine 0 bedeutet einfach, daß die entsprechende Proportion an anderer Stelle gesetzt wurde - 0 bedeutet eigentlich so viel wie »entsprechend anpassen«.

Die Gesamtsumme aller weightx-Rahmenbedingungen ist jetzt 100, und Sie können sich nun den weighty-Eigenschaften widmen. Hier gibt es drei Zeilen. Wenn Sie einen Blick auf das von Ihnen gezeichnete Raster werfen, sieht es so aus, als ob etwa 20 Prozent auf die Schaltfläche und die restlichen 80 (40 Prozent pro Zeile) auf die Textfelder verteilt sind. Sie müssen den Wert, wie bei den x-Werten, jeweils nur für eine Zelle pro Zeile setzen (die beiden Labels und die Schaltfläche), während alle anderen Zellen als weightx 0 haben.

Hier die endgültigen fünf Aufrufe an buildConstraints() mit den entsprechenden Gewichtungen:

buildConstraints(constraints, 0, 0, 1, 1, 10, 40); // Label für das Namensfeld
buildConstraints(constraints, 1, 0, 1, 1, 90, 0);  //Textfeld für den Namen
buildConstraints(constraints, 0, 1, 1, 1, 0, 40);  //Label für das Paßwort-Feld
buildConstraints(constraints, 1, 1, 1, 1, 0, 0);   //Textfeld für das Paßwort
buildConstraints(constraints, 0, 2, 2, 1, 0, 20);  //OK-Schaltfläche

Abbildung 12.9 zeigt das Ergebnis mit den richtigen Proportionen.


Abbildung 12.9:
GridBagLayout - zweiter Arbeitsgang

Dieser Schritt zielt darauf ab, ein paar einfache Proportionen für die räumliche Anordnung der Zeilen und Zellen am Bildschirm zu erstellen, wodurch Ihnen eine grobe Einschätzung der Größe der verschiedenen Komponenten ermöglicht wird, allerdings sollten Sie an dieser Stelle mit vielen Versuchen und Fehlern rechnen.

Vierter Schritt: Komponenten einfügen und anordnen

Wenn das Layout und die Proportionen entsprechend vorbereitet sind, können Sie die Platzhalter-Schaltflächen durch richtige Label und Textfelder ersetzen. Da Sie hierfür bereits alles vorbereitet haben, sollte das problemlos funktionieren, richtig? Nicht ganz. Abbildung 12.10 zeigt Ihnen das Resultat, wenn Sie dieselben vorherigen Rahmenbedingungen benutzen und die Schaltflächen durch richtige Komponenten ersetzen.


Abbildung 12.10:
GridBagLayout - fast geschafft

Dieses Layout kommt der Sache nahe, aber es sieht sonderbar aus. Die Textfelder sind zu hoch, und die Schaltfläche OK dehnt die Breite der Zelle.

Was jetzt noch fehlt, sind die Rahmenbedingungen, die für die Anordnung der Komponenten in der Zelle sorgen. Es gibt davon zwei: fill und anchor.

Die Rahmenbedingung fill legt für Komponenten, die sich in jede Richtung ausdehnen können (wie Textfelder und Schaltflächen), die Ausdehnungsrichtung fest. fill kann einen von vier als Klassenvariablen in der Klasse GridBagConstraints definierten Werten haben:

Denken Sie daran, daß dieses Layout dynamisch ist. Sie richten nicht die tatsächlichen Pixel-Maße von Komponenten ein, sondern bestimmen, in welcher Richtung sich diese Elemente bei einem bestimmten Panel von beliebiger Größe ausdehnen können.

Die Standard-Rahmenbedingung für fill ist für alle Komponenten NONE. Warum also füllen die Textfelder und Label die Zellen? Gehen Sie in Gedanken noch einmal zurück zum Beginn des Codes für dieses Beispiel, wo der init()-Methode folgende Zeile hinzugefügt wurde:

constraints.fill = GridBagConstraints.BOTH;

Jetzt verstehen Sie ihre Bedeutung. Bei der endgültigen Version dieses Applets entfernen Sie diese Zeile und fügen jeder eigenständigen Komponente die fill-Werte hinzu.

Die zweite für das Aussehen einer Komponente in der Zelle maßgebliche Rahmenbedingung ist anchor. Diese Rahmenbedingung gilt nur für Komponenten, die nicht die gesamte Zelle füllen, und weist das AWT an, wo die Komponente innerhalb der Zelle auszurichten ist. Die möglichen Werte für die anchor-Rahmenbedingung sind GridBagConstraints.CENTER, wodurch die Komponente innerhalb der Zelle sowohl vertikal als auch horizontal zentriert ausgerichtet wird, oder einer von acht Ausrichtungswerten: GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST, GridBagConstraints.EAST, GridBagConstraints.SOUTHEAST , GridBagConstraints.SOUTH, GridBagConstraints.SOUTHWEST, GridBagConstraints.WEST , oder GridBagConstraints.NORTHWEST. Der Standardwert für Anchor ist GridBagConstraints.CENTER.

Diese Rahmenbedingungen setzen Sie genau wie alle anderen: indem Sie die Instanzvariablen im GridBagConstraints-Objekt ändern. Sie können die Definition von buildConstraints() zur Aufnahme von zwei weiteren Argumenten (es handelt sich um int-Werte) ändern, oder Sie können diese einfach innerhalb der init()-Methode setzen. Es empfiehlt sich letzteres.

Behandeln Sie Standard-Werte mit Vorsicht. Denken Sie daran, daß, bedingt durch die erneute Verwendung desselben GridBagConstraints-Objekts für jede Komponente, ein paar Werte übrig bleiben können, nachdem Sie die Bearbeitung einer Komponente abgeschlossen haben. Wenn andererseits der Wert der fill- oder anchor-Eigenschaft von einem Objekt der gleiche ist wie bei der vorherigen Komponente, haben Sie den Vorteil, das Objekt nicht zurücksetzen zu müssen.

Für ein entsprechendes Beispiel nehmen wir drei Änderungen an den fill- und anchor -Eigenschaften der Komponenten vor:

Der entsprechende Code hierfür wird Ihnen im folgenden gezeigt; der vollständige Code für das Beispiel ist am Ende dieses Abschnitts aufgeführt. Sie können die vorgenommenen Änderungen erkennen.

Fünfter Schritt: Die letzten Anpassungen

Dieser Schritt wurde der Liste hinzugefügt, da die Erfahrung bei der Erstellung von Layouts mit GridBagLayout gezeigt hat, daß, auch wenn man alle erforderlichen Schritte ausführt, das entstandene Layout sich meist doch nicht so ganz richtig darstellt, und »Herumspielen« mit den verschiedenen Werten bzw. entsprechendes »Zurechtbasteln« der Rahmenbedingungen notwendig ist, um letztendlich das gewünschte Ergebnis zu erhalten. Das ist vollkommen in Ordnung; das Ziel der obigen vier Schritte war es, die endgültige Anordnung so gut wie möglich vorzubereiten und nicht jedes Mal ein perfektes Layout zu entwerfen.

Listing 12.5 zeigt den vollständigen Code für das Layout, das Sie in diesem Abschnitt erstellt haben. Wenn jetzt noch Unklarheiten bestehen, sollten Sie diesen Code zeilenweise durcharbeiten, um ein klares Verständnis der verschiedenen Teile sicherzustellen.

Listing 12.5: Der gesamte Quelltext von NamePass.java

 1: import java.awt.*;
 2:
 3: public class NamePass extends java.applet.Applet {
 4:
 5:   void buildConstraints(GridBagConstraints gbc, int gx, int gy,
 6:       int gw, int gh, int wx, int wy) {
 7:
 8:       gbc.gridx = gx;
 9:       gbc.gridy = gy;
10:       gbc.gridwidth = gw;
11:       gbc.gridheight = gh;
12:       gbc.weightx = wx;
13:       gbc.weighty = wy;
14:   }
15:
16:   public void init() {
17:       GridBagLayout gridbag = new GridBagLayout();
18:       GridBagConstraints constraints = new GridBagConstraints();
19:       setLayout(gridbag);
20:
21:       // Der Label für das Textfeld des Namens
22:       buildConstraints(constraints, 0, 0, 1, 1, 10, 40);
23:       constraints.fill = GridBagConstraints.NONE;
24:       constraints.anchor = GridBagConstraints.EAST;
25:       Label label1 = new Label("Name:", Label.LEFT);
26:       gridbag.setConstraints(label1, constraints);
27:       add(label1);
28:
29:       // Das Textfeld für den Namen
30:       buildConstraints(constraints, 1, 0, 1, 1, 90, 0);
31:       constraints.fill = GridBagConstraints.HORIZONTAL;
32:       TextField tfname = new TextField();
33:       gridbag.setConstraints(tfname, constraints);
34:       add(tfname);
35:
36:       // Der Label für das Textfeld des Paßwortes
37:       buildConstraints(constraints, 0, 1, 1, 1, 0, 40);
38:       constraints.fill = GridBagConstraints.NONE;
39:       constraints.anchor = GridBagConstraints.EAST;
40:       Label label2 = new Label("Password:", Label.LEFT);
41:       gridbag.setConstraints(label2, constraints);
42:       add(label2);
43:
44:       // Das Textfeld des Paßwortes
45:       buildConstraints(constraints, 1, 1, 1, 1, 0, 0);
46:       constraints.fill = GridBagConstraints.HORIZONTAL;
47:       TextField tfpass = new TextField();
48:       tfpass.setEchoCharacter('*');
49:       gridbag.setConstraints(tfpass, constraints);
50:       add(tfpass);
51:
52:       // Die OK-Schaltfläche
53:       buildConstraints(constraints, 0, 2, 2, 1, 0, 20);
54:       constraints.fill = GridBagConstraints.NONE;
55:       constraints.anchor = GridBagConstraints.CENTER;
56:       Button okb = new Button("OK");
57:       gridbag.setConstraints(okb, constraints);
58:       add(okb);
59:   }
60: }

Das folgende <APPLET>-Tag wurde verwendet, um dieses Applet zu testen:

<applet code="NamePass.class" height=180 width=240>
</applet>

Wenn Sie dieses Applet kompilieren, wird der Aufruf der Methode setEchoCharacter() in Zeile 48 eine deprecation-Warnung erzeugen, da diese Methode in den Java- Versionen nach 1.02 umbenannt wurde. Sie kann durch die Methode setEchoChar() ersetzt werden, wenn Sie ein Applet für die Version 1.2 der Sprache schreiben.

ipadx und ipady

Bevor wir zum Abschluß des GridBagLayout kommen (sind wir noch nicht fertig?), müssen noch zwei weitere Rahmenbedingungen erwähnt werden: ipadx und ipady. Diese beiden Rahmenbedingungen kontrollieren das Auffüllen - hier geht es um den zusätzlichen Raum rund um eine einzelne Komponente. Standardmäßig ist kein zusätzlicher Raum um die Komponenten vorgegeben (was man am besten bei Komponenten, die ihre Zellen füllen, sehen kann).

Mit ipadx fügt man jeder Seite der Komponente Platz hinzu, und mit ipady fügt man entsprechenden Platz oben und unten ein.

Eckeinsätze (Insets)

Bei der Erstellung eines neuen Layout-Managers (oder bei der Verwendung von ipadx und ipady in GridBagLayout) erzeugte horizontale und vertikale Abstände dienen zur Bestimmung des Platzes zwischen Komponenten in einem Panel. Eckeinsätze hingegen werden zur Festlegung des Rands um das Panel selbst benutzt. Die Klasse Insets bietet Eckeinsatzwerte für oben, unten, links und rechts, die dann verwendet werden, wenn das Panel gezeichnet wird.


Eckeinsätze (Insets) dienen zur Bestimmung des Platzes zwischen den Kanten eines Panels und seinen Komponenten.

Um Ihrem Layout einen Eckeinsatz hinzuzufügen, überschreiben Sie entweder die Methode insets() oder getInsets() in Ihrer Applet-Klasse oder einer anderen Panel- Klasse. Verwenden Sie für Java Version 1.02 insets(); und ab der Version 1.1 getInsets() . Sie erfüllen den gleichen Zweck, lediglich der Name wurde geändert.

Erstellen Sie mit der Methode insets() oder getInsets() ein neues Insets-Objekt, wobei der Konstruktor für die Insets-Klasse vier ganzzahlige Werte erwartet, die für die Eckeinsätze oben, unten, links und rechts im Panel stehen. Die Methode insets() sollte dann das Insets-Objekt ausgeben. Es folgt ein Beispiel mit Java-1.02-konformem Code zum Einfügen von Eckeinsätzen für ein GridLayout mit den Werten: 10 oben und unten und 30 links und rechts. Abbildung 12.11 zeigt das Ergebnis.

public Insets insets() {
return new Insets(10, 30, 10, 30); 
}


Abbildung 12.11:
Ein Panel mit Eckeinsätzen mit 10 Pixeln oben und unten und 30 Pixeln links und rechts

Zusammenfassung

Abstrakter Expressionismus geht nur so weit, wie Sie das während des heutigen Tages gesehen haben. Layout-Manager benötigen eine gewisse Feinabstimmung für Leute, die eine genauere Kontrolle über die Plazierung von Komponenten, die auf einer Benutzerschnittstelle erscheinen, gewohnt sind.

Sie wissen nun, wie Sie die fünf verschiedenen Layout-Manager verwenden. Wenn Sie mit dem Abstract Windowing Toolkit arbeiten, werden Sie erkennen, daß dieses jede Art von Benutzerschnittstelle durch verschachtelte Container und unterschiedliche Layout-Manager annähern kann.

Sobald Sie die Entwicklung einer Benutzerschnittstelle in Java gemeistert haben, bietet Ihr Programm etwas, was die meisten anderen visuellen Programmiersprachen nicht bieten: eine Benutzerschnittstelle, die ohne Veränderung auf vielen Plattformen läuft.

Um es mit einem oft zitierten Ausspruch zu sagen: »Ich weiß nicht, ob es Kunst ist, aber mir gefällt es.«

Fragen und Antworten

Frage:
Ich mag es überhaupt nicht, mit Layout-Managern zu arbeiten. Entweder sind sie zu einfach von den Möglichkeiten her oder zu kompliziert in der Anwendung (GridBagLayout). Selbst dann, wenn ich viel herumbastle, sehen mein Applets nicht so aus, wie ich mir das vorgestellt habe. Alles was ich will, ist, die Größe meiner Komponenten festzulegen und diese dann an einer bestimmten x,y-Position auf dem Bildschirm auszugeben. Kann ich das?

Antwort:
Es ist möglich, aber sehr problematisch. Das AWT wurde so entworfen, daß die grafische Benutzerschnittstelle eines Programms gleichgut auf unterschiedlichen Plattformen und verschiedenen Bildschirmauflösungen, Schriften, Bildschirmgrößen usw. funktioniert. Wenn Sie sich auf Pixel-Koordinaten verlassen, kann das dazu führen, daß ein Programm auf der einen Plattform gut aussieht und auf anderen nicht zu verwenden ist, da sich einzelne Komponenten überdecken, von den Kanten des Containers abgeschnitten werden usw. Layout-Manager umgehen dieses Problem, indem sie Komponenten dynamisch auf dem Bildschirm plazieren. Obwohl es gewisse Unterschiede im Endergebnis auf den verschiedenen Plattformen gibt, sind diese alles andere als eine Katastrophe.

Immer noch nicht überzeugt? Verwenden Sie null als Layout-Manager und die Methode reshape(), um einer Komponente eine bestimmte Größe zu geben und sie an einer bestimmten Position zu plazieren.

setLayout(null);
Button myButton = new Button("OK");
myButton.reshape(10, 10, 30, 15);

Mehr Informationen zu reshape() erhalten Sie in der Dokumentation der Klasse Component.

Frage:
Ich habe mir die AWT-Klassen angesehen und ein Paket namens Peer entdeckt. Außerdem wird an vielen Stellen in der API-Dokumentation auf die Peer-Klassen verwiesen. Was bewirken diese Klassen?

Antwort:
Peers sind für die plattformspezifischen Teile des AWT zuständig. Wenn Sie beispielsweise ein AWT-Fenster erstellen, haben Sie eine Instanz der Window- Klasse, die allgemeine Fenstereigenschaften bereitstellt. Daneben gibt es eine Instanz der WindowPeer-Klasse, die ein sehr spezifisches Fenster für diese Plattform - ein Motiv-Fenster unter XWindows, ein Macintosh-Fenster für den Macintosh oder ein Fenster für Windows 95 - erstellt. Diese »Peer«-Klassen handhaben auch die Kommunikation zwischen dem Fenstersystem und dem Java-Fenster selbst. Durch Trennen der allgemeinen Komponenteneigenschaften (AWT-Klassen) von der eigentlichen Systemimplementierung und dem -aussehen (Peer-Klassen) können Sie sich auf das Verhalten Ihrer Java- Anwendung konzentrieren und die plattformspezifischen Einzelheiten der Java-Implementierung überlassen.



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH.
Elektronische Fassung des Titels: Java 2 in 21 Tagen, ISBN: 3-8272-5578-3