Previous Page TOC Index Next Page See Page

13

Fortgeschrittene Benutzeroberflächen mit dem AWT

von
Laura Lemay

Durch Hinzufügen von Komponenten der Benutzeroberfläche kommt Leben in Ihr Applet. Richtig interessant wird es aber, wenn Sie weiter in die fortgeschrittenen Teile des Toolkit einsteigen und ausgeklügeltere Oberflächen und Programme erstellen. Heute bauen wir auf dem auf, was Sie gestern über Komponenten, Layout-Manager und Ereignisse der Benutzeroberfläche gelernt haben. Und ich stelle Ihnen einige neue Konzepte von ATW vor:

Neben allen diesen faszinierenden Themen arbeiten wir in der Mitte dieser Lektion ein komplexes Beispiel eines Applet durch, das mehrere Komponenten, Klassen und Layouts einsetzt. Dabei bekommen Sie ein Gefühl dafür, wie ein komplexes Applet zusammengesetzt wird.

Komponenten verschachteln

Beginnen wir mit etwas Leichtem. Gestern haben Sie gelernt, wie Komponenten in ein Applet eingefügt werden, wobei der Layout-Manager des Applet festlegt, wie diese Komponenten innerhalb des Applet angeordnet werden. Ein Applet ist eine Art Panel, das wiederum eine Art Container ist. Diese Vererbungshierarchie macht es möglich, Objekte in ein Applet einzubinden.

Wenn Sie einzelne Komponenten in ein Applet einfügen, gibt es keine Einschränkungen. AWT-Panels, wozu auch ein Applet gehört, können andere Panels enthalten und Sie können Panels nach Bedarf verschachteln. Jedes Panel hat seinen eigenen Layout-Manager, was bedeutet, Sie können verschiedene Layouts für unterschiedliche Teile des Applet-Bereiches erstellen, Hinter- und Vordergrundfarben sowie Fonts auf einzelne Applet-Teile begrenzen und das Design der Komponenten Ihrer Benutzeroberfläche individuell in verschiedenen Gruppen auslegen. Je komplexer das Layout Ihres Applet ist, um so mehr bietet sich das Verschachteln von Panels an.

Verschachtelte Panels

Wie Sie bereits wissen, sind Panels Komponenten, die am Bildschirm angezeigt werden können; Container, die Superklasse von Panel, bietet die allgemeinen Eigenschaften zum Anlegen weiterer Komponenten. Die Applet-Klasse, von der alle Ihre Applets abgeleitet werden, ist eine Subklasse von Panel. Um andere Panels in einem Applet zu verschachteln, erstellen Sie eine neue Panel-Komponente und fügen Sie in das Applet ein wie jede andere Komponente der Benutzeroberfläche:

setLayout(new GridLayout(1, 2, 10, 10));

Panel panel1 = new Panel();
Panel panel2 = new Panel();
add(panel1);
add(panel2);

Sie können dann für diese untergeordneten Panels ein unabhängiges Layout wählen und ATW-Komponenten (und weitere untergeordnete Panels) einfügen, indem Sie die add()-Methode im entsprechenden Panel aufrufen:

panel1.setLayout(new FlowLayout());

panel1.add(new Button("Up"));
panel1.add(new Button("Down"));

Dies können Sie zwar alles in einer Klasse realisieren, jedoch ist manchmal schwierig festzustellen, welchem Panel Sie eine Komponente hinzufügen. Es ist bei Applets und Anwendungen üblich, die starken Gebrauch von untergeordneten Panels machen, das Layout und Verhalten der untergeordneten Panels in separaten Klassen anzulegen und dann die Panels mit Methoden miteinander kommunizieren zu lassen. Später werden Sie in dieser Lektion ein umfangreiches Beispiel im Abschnitt »Komplettes Beispiel: RGB/HSB-Konverter« sehen.

Verschachtelte Panels und das 1.02-Ereignismodell

Bei einem Applet mit verschachtelten Panels bilden diese Panels eine Hierarchie vom äußersten Panel (normalerweise dem Applet) bis zur innersten Komponente der Benutzeroberfläche. Dies Hierarchie ist wichtig für die Art, wie die einzelnen Komponente der Oberfläche miteinander oder interagieren. So bestimmte die Komponentenhierarchie beispielsweise die Reihenfolge, in der die Komponenten am Bildschirm ausgegeben werden. Noch wichtiger ist jedoch, daß die Komponentenhierarchie sich auch die Ereignisbearbhandlung in 1.02, insbesondere bei Low-Level-Ereignissen, wie Maus- und Tastatureingaben, auswirkt.

Ereignisse im 1.02-Ereignismodell werden von der innersten Komponente in der Komponentenhierarchie entgegengenommen und in der Hierarchie bis zum Panel des Applet (oder dem Fenster der Wurzel bei Java-Applikationen) weitergereicht. Nehmen wir beispielsweise an, Sie haben ein Applet mit einem untergeordneten Panel, das Mausereignisse behandelt, und daß das Panel eine Schaltfläche enthält. Durch Anklicken der Schaltfläche erhält die Schaltfläche das Ereignis, bevor es das Panel erreicht. Ist die Schaltfläche nicht an dem mouseDown()-Ereignis interessiert, wird das Ereignis an das Panel weitergereicht, das den Vorgang dann bearbeitet oder in der Hierarchie weiter nach oben gibt.

Aus diesen Gründen müssen Ihre Ereignisverarbeitungsmethoden im 1.02-Ereignismodell die booleschen Werte true oder false zurückgeben. Dieser Wert bestimmt, wie das Ereignis in der Hierarchie von einer Komponente zu einer anderen gelangt. Der Wert true bedeutet, daß das Ereignis »verbraucht« ist – die Komponente hat das Ergebnis verarbeitet und niemand anders soll es sehen. Der Rückgabewert false bedeutet, daß das Ereignis in der Hierarchie weiter nach oben geleitet werden soll, so daß jemand anders es verarbeiten kann. AWT verläßt sich auf diese Rückgabewerte, um sicherzustellen, daß die Ereignisse verarbeitet werden. Daher ist es so wichtig, diese Rückgabewerte richtig zu erhalten (und die Schwierigkeiten hierbei sind ein Grund dafür, daß das 1.1-Ereignismodell einen völlig anderen Mechanismus nutzt).


Die Auswirkungen von Ereignissen, die von einer Komponente zur nächsten Komponente der Hierarchie weitergeleitet werden, gelten nur beim 1.02-Ereignismodell. Auch wenn Sie bei 1.1 noch existiert, bestimmt die Komponentenhierarchie nicht, wie Ereignisse behandelt werden. Die Listener-Registrierung hat hier den hierarchiebasierten Ereignisverteilungsmechanismus abgelöst.

Verschiedene Komponenten der Benutzeroberfläche

Nachdem Sie die grundlegenden Komponenten der Benutzeroberfläche beherrschen und wissen, wie sie in Panels eingefügt, ihr Layout angeordnet und ihre Ereignisse gehandhabt werden, können Sie noch weitere Komponenten in Ihre Oberfläche aufnehmen. In diesem Abschnitt lernen Sie sechs weitere Komponenten kennen, die Sie in Ihren Applets benutzen können, um andere Elemente der Benutzeroberfläche zu erstellen: Textbereiche, Listenfelder, Rollfelder, Bildlaufleisten, Zeichenbereiche und Cursor.

Textbereiche

Textbereiche unterscheiden sich von Textfeldern dadurch, daß sie mehr Funktionalität zur Handhabung größerer Textmengen bieten. Da die Größe von Textfeldern begrenzt ist und Textfelder nicht gescrollt werden können, eignen sie sich besser für einzeilige Eingaben. Textbereiche können eine beliebige Breite und Höhe haben. Standardmäßig haben Sie auch Bildlaufleisten, so daß größere Textmengen leicht untergebracht werden können.


Textbereiche sind größere, mit Bildlaufleisten versehene Komponenten zur Texteingabe. Im Gegensatz zu Textfeldern, die nur eine Zeile Text liefern, können Textbereiche eine beliebige Menge editierbaren Text aufnehmen.

Um einen Textbereich zu erstellen, verwenden Sie einen der folgenden Konstruktoren:

Mit dem folgenden Eingabecode können Sie einen einfachen Textbereich erstellen (das funktioniert unter 1.02 und 1.1). Die Ergebnisse sind ind Abbildung 13.1 gezeigt.

import java.awt.*;


public class TextAreaTest extends java.applet.Applet {

public void init() {
String str =
"Once upon a midnight dreary, while I pondered, weak and weary,\n" +
"Over many a quaint and curious volume of forgotten lore,\n" +
"While I nodded, nearly napping, suddenly there came a tapping,\n" +
"As of some one gently rapping, rapping at my chamber door.\n" +
"\"'Tis some visitor,\" I muttered, \"tapping at my chamber door-\n" +
"Only this, and nothing more.\"\n\n";
// mehr Text gelöscht

add(new TextArea(str));
}
}

siehe Abbildung

Abbildung 13.1:
Textbereich

Sowohl Textbereiche als auch Textfelder sind von der Klasse TextComponent abgeleitet, so daß ein großer Teil des Verhaltens von Textfeldern (insbesondere das Ermitteln, Festlegen und die Auswahl von Text) auch bei Textbereichen genutzt werden kann (siehe Tabelle 12.4). Textbereiche verfügen auch über eine Reihe eigener nützlicher Methoden. Tabelle 13.1 zeigt eine Auswahl dieser Methoden.

Tabelle 13.1: Methoden für Textbereiche

Methode

Aktion

getColumns()

Gibt die Breite des Textbereiches in Zeichen oder Spalten zurück

getRows()

Gibt die Anzahl der Zeilen im Textbereich (nicht die Anzahl der Textzeilen, die der Textbereich enthält) zurück

insertText(String, int)

Fügt an der angegebenen Position eine Zeichenkette in den Text ein (Textpositionen beginnen bei 0) (nur bei 1.02; in 1.1 durch insert() ersetzt)

insert(String, int)

Fügt an der angegebenen Position eine Zeichenkette in den Text ein (Textpositionen beginnen bei 0) (nur bei 1.1)

replaceText(String, int, int)

Ersetzt den Text zwischen den angegebenen ganzzahligen Positionen durch eine andere Zeichenkette (nur bei 1.02; in 1.1 durch replace() ersetzt)

replace(String, int, int)

Ersetzt eine Zeichenkette durch eine andere Zeichenkette (nur bei 1.1)

Textbereiche haben die gleichen Ereignisse wie Textfelder. Bei 1.02 sind das Fokuserhalt und Fokusverlust; bei 1.1 gibt es auch Textänderungsereignisse.

Bei 1.02 können Sie die methoden gotFocus() und lostFocus() benutzen, um Fokusereignisse zu verfolgen:

public boolean gotFocus(Event evt, Object arg) {

...
}

public boolean lostFocus(Event evt, Object arg) {
...
}

Beim 1.1 Ereignismodell implementieren Sie die Methode focusGained() oder focusLost(), die Teil der FocusListener-Schnittstelle sind, um Fokusereignisse abzufangen. Benutzen Sie die addFocusListener()-Methode, um einen Fokus-Listener in einen Textbereich einzutragen.

Bei neuen Textänderungsereignissen müssen Sie die TextListener-Schnittstelle implementieren, die die Methode textValueChanged() enthält. Benutzen Sie addTextListener(), um den Listener in einem Textbereich zu registrieren.

Listenfelder

Erinnern Sie sich an das Auswahlmenü, ein Pop-up-Menü, in dem mehrere Optionen zur Wahl angeboten werden? Ein Listenfeld hat eine ähnliche Funktionalität, da wie in einem Auswahlmenü verschiedene Optionen in der Liste ausgewählt werden können. Listenfelder unterschieden sich von Auswahlmenüs jedoch durch zwei wesentliche Aspekte:


Listenfelder stellen eine Auswahl von Elementen bereit, die selektiert oder deselektiert werden können. Im Gegensatz zu Auswahlmenüs sind Listenfelder keine Pop-up-Menüs und können so definiert werden, daß eine Mehrfachauswahl möglich ist.

Um ein Listenfeld zu erzeugen, legen Sie eine Instanz der List-Klasse an und fügen dann die einzelnen Elemente in die Liste ein. Die List-Klasse hat folgende Konstruktoren:

Nachdem Sie ein List-Objekt erstellt haben, fügen Sie Einträge ein und verwenden dazu – je nach dem, ob Sie 1.02 oder 1.1 einsetzen – eine der beiden folgenden Methoden: Bei 1.02 benutzen Sie die addItem()-Methode, um Elemente in die Liste einzutragen. Bei 1.1 benutzen Sie add(). Beide Methoden nehmen erwarten ein Argument, nämlich eine Zeichenkette, die den Namen des Eintrags angibt.

Hier ist ein Listenfeld mit zwei Elementen aus 1.02:

List theList = new List();

theList.addItem("night");
theList.addItem("day");

Hier die gleiche Liste in 1.1:

List theList = new List();

theList.add ("night");
theList.add ("day");

Nachdem Sie die Liste erstellt und in ihr Elemente eingetragen haben, fügen Sie das gesamte List-Objekt in die Komponente ein, die es enthalten soll. Dieses Bespiel aus 1.02 erzeugt eine Liste mit fünf Einträgen, bei der die Auswahl mehrere Optionen zulässig ist. Das Ergebnis dieses Codes ist in Abbildung 13.2 dargestellt.

import java.awt.*;


public class ListsTest extends java.applet.Applet {

public void init() {
List lst = new List(5, true);
// bei 1.1 ändern Sie addItem in add
lst.addItem("Hamlet");
lst.addItem("Claudius");
lst.addItem("Gertrude");
lst.addItem("Polonius");
lst.addItem("Horatio");
lst.addItem("Laertes");
lst.addItem("Ophelia");

add(lst);
}
}

siehe Abbildung

Abbildung 13.2:
Listenfeld

Tabelle 13.2 zeigt einige der Methoden, die bei Listenfelder zur Verfügung stehen. Eine Gesamtübersicht finden Sie in der API-Dokumentation.

Tabelle 13.2: Methoden für Listenfelder

Methode

Aktion

getItem(int)

Gibt die Zeichenkette an der angegebenen Position aus

countItems()

Gibt die Anzahl der Elemente in der Liste aus (nur bei 1.02; bei 1.1 benutzen Sie getItemCount())

getItemCount()

Gibt die Anzahl der Elemente in der Liste aus (nur bei 1.1)

getSelectedIndex()

Gibt die Indexposition des Elementes zurück, das ausgewählt wurde (bei Listen, die nur eine einzele Option zur Auswahl zugelassen ist)

getSelectedIndexes()

Gibt ein Array von Indexpositionen zurück (bei Listen, die Mehrfachauswahl zulassen)

getSelectedItem()

Gibt das derzeit gewählte Element als Zeichenkette zurück

getSelectedItems()

Gibt ein Array von Zeichenketten aller gewählten Elemente
zurück

select(int)

Wählt das Element an der angegebenen Position aus

Listenfelder erzeugen drei unterschiedliche Arten von Ereignissen: Selektieren oder Deselektieren eines individuellen Listenelementes führt zu einem Listenselektions- oder Listendeselektionsereignis, und Doppelklicken auf ein Listenelement führt zu einem Aktionsereignis.

Beim 1.02-Ereignismodell können Sie das action()-Methode überschreiben, so daß sie ein doppelt angeklicktes Listenelement behandelt. Bei Listenselektion und Listendeselektion müssen Sie handleEvent() überschreiben und auf das Ereignis IDs LIST_SELECT bzw. LIST_DESELECT hin testen.

Beim 1.1-Ereignismodell können Aktionsmethoden bei einem Listenfeld genauso gehandhabt werden wie bei anderen Komponenten: durch Implementieren von actionPerformed() in der ActionListener-Schnittstelle und anschließenden Aufruf von addActionListener() für das Listenfeld, um den entsprechenden Ereignis-Listener einzutragen.

Bei Listenselektions- und Listendeselektionsereignissen benutzen Sie die itemStateChanged()-Methode in der ItemListener-Schnittstelle. Die ItemEvent-Klasse enthält Methoden für getItem() und getStateChange(), die Ihnen sagen können, welches Element das Ereignis erhalten hat, und ob es selektiert oder deselektiert wurde.

Bildlaufleisten und Schieber

Textbereiche, Listenfelder und Rollfelder kommen mit ihren eigenen Bildlaufleisten, die in diese Komponenten eingebaut sind und es Ihnen ermöglichen, sowohl den Inhalt des Bereiches, Panels bzw. der Liste sowie die Bildlaufleisten als eine Einheit zu handhaben. Sie können auch individuelle Bildlaufleisten oder Schieber erstellen, um einen Wertebereich zu manipulieren oder eine andere Art scrollbarer Komponenten zu implementieren.

Bildlaufleisten werden zur Auswahl eines Wertes zwischen einem Höchst- und Mindestwert verwendet. Zur Änderung des aktuellen Wertes der Bildlaufleiste können Sie die folgenden drei Teile der Bildlaufleiste benutzen (siehe Abbildung 13.3):

siehe Abbildung

Abbildung 13.3:
Teile der Bildlaufleiste

Durch Auswahl dieser visuellen Elemente wird der Wert in der Bildlaufleiste geändert. Sie brauchen nichts zu aktualisieren und auch keine Ereignisse zu verwalten. Nötig ist nur die Festlegung eines Höchst- und Mindestwertes für die Bildlaufleiste. Der Rest wird von Java erledigt.


Eine Bildlaufleiste ist ein visuelles Element der Benutzeroberfläche, daß Ihnen die Auswahl zwischen einem Minimum und einem Maximum ermöglicht. Bildlaufleisten werden manchmal auch als Schieber bezeichnet.

Um eine Bildlaufleiste zu erstellen, können Sie einen der folgenden drei Konstruktoren verwenden:

Hier finden Sie jetzt ein Beispiel einer Bildlaufleiste, die in Java 1.1 einen einzelnen Wert erhöht (siehe Abbildung 13.4). Das Label links neben der Bildlaufleiste wird jedesmal aktualisiert, wenn sich der Wert der Bildlaufleiste ändert.

import java.awt.*;

import java.awt.event.*;

public class SliderTest extends java.applet.Applet
implements AdjustmentListener {
Label l;

public void init() {
setLayout(new GridLayout(1,2));
l = new Label("1", Label.CENTER);
add(l);
Scrollbar sb = new
Scrollbar(Scrollbar.HORIZONTAL,0,0,1,100);
sb.addAdjustmentListener(this);
add(sb);
}

public Insets getInsets() {
return new Insets(15,15,15,15);
}

public void adjustmentValueChanged(AdjustmentEvent e) {
int v = ((Scrollbar)e.getSource()).getValue();
l.setText(String.valueOf(v));
repaint();
}

}

siehe Abbildung

Abbildung 13.4:
Bildlaufleiste

Die Scrollbar-Klasse bietet mehrere Methoden zum Handhaben der Werte innerhalb der Bildlaufleisten. Diese Methoden sind in Tabelle 13.3 aufgeführt.

Tabelle 13.3: Scrollbar-Methoden

Methode

Aktion

getMaximum()

Gibt den Höchstwert zurück

getMinimum()

Gibt den Mindestwert zurück

getOrientation()

Gibt die Ausrichtung dieser Bildlaufleiste zurück: 0 ist Scrollbar.HORIZONTAL; 1 ist Scrollbar.VERTICAL

getValue()

Gibt den aktuellen Wert der Bildlaufleiste zurück

setValue(int)

Bestimmt den aktuellen Wert der Bildlaufleiste

setLineIncrement(int inc)

Ändert das Inkrement, wie weit gescrollt werden soll, wenn die Schaltfläche an einem der Enden der Bildlaufleiste gewählt wird. Voreinstellung ist 1 (nur bei 1.02; in 1.1 durch setUnitIncrement() ersetzt)

setUnitIncrement(int inc)

Ändert das Inkrement, wie weit gescrollt werden soll, wenn die Schaltfläche an einem der Enden der Bildlaufleiste gewählt wird. Voreinstellung ist 1 (nur bei 1.1)

getLineIncrement()

Gibt das Inkrement zurück, wie weit gescrollt werden soll, wenn die Schaltfläche an einem der Enden der Bildlaufleiste gewählt wird (nur bei 1.02; in 1.1 durch getUnitIncrement() ersetzt)

getUnitIncrement(int inc)

Gibt das Inkrement zurück, wie weit gescrollt werden soll, wenn die Schaltfläche an einem der Enden der Bildlaufleiste gewählt wird (nur bei 1.1)

setPageIncrement(int inc)

Ändert das Inkrement, wie weit gescrollt werden soll, wenn einer der Bereiche innerhalb Bildlaufleiste (zwischen dem Schieber und der einer der Schaltflächen) gewählt wurde. Voreinstellung ist 10 (nur bei 1.02; in 1.1 durch setBlockIncrement ersetzt)

setBlockIncrement(int inc)

Ändert das Inkrement, wie weit gescrollt werden soll, wenn der Bereich innerhalb der Bildlaufleiste gewählt wurde. Voreinstellung ist 10 (nur bei 1.1)

getPageIncrement()

Gibt das Inkrement zurück, wie weit gescrollt werden soll, wenn der Bereich innerhalb der Bildlaufleiste gewählt wurde (nur bei 1.02; in 1.1 durch getBlockIncrement ersetzt.)

getBlockIncrement()

Gibt das Inkrement zurück, wie weit gescrollt werden soll, wenn der Bereich innerhalb der Bildlaufleiste gewählt wurde (nur bei 1.1)

Wenn Sie den Umgang mit Ereignissen mögrn, werden Sie Bildlaufleisten lieben. Eine ganze Reihe von Ereignissen wird von verschiedenen Bildlaufleistenbewegungen erzeugt und behandelt. Beim 1.02 Ereignismodell müssen Sie für alle diese Ereignisse handleEvent() verwenden. Tabelle 13.4 zeigt die Ereignis-IDs, nach denen gesucht wird und die Bewegung, die diese auslöst.

Tabelle 13.4: Bildlaufleistenereignisse

Ereignis-ID

Steht für

SCROLL_ABSOLUTE

Wird erzeugt, wenn der Schieber der Bildlaufleiste verschoben wird

SCROLL_LINE_DOWN

Wird erzeugt, wenn die untere bzw. linke Schaltfläche der Bildlaufleiste gewählt wird

SCROLL_LINE_UP

Wird erzeugt, wenn die obere bzw. rechte Schaltfläche der Bildlaufleiste gewählt wird

SCROLL_PAGE_DOWN

Wird erzeugt, wenn der Bereich der Bildlaufleiste unterhalb (oder links neben) des Schiebers gewählt wird

SCROLL_PAGE_UP

Wird erzeugt, wenn der Bereich der Bildlaufleiste oberhalb (oder rechts neben) des Schiebers gewählt wird

Beim 1.1-Ereignismodell machen Bildlaufleistenereignisse nicht ganz so viel Spaß. Alle Bildlaufleistenereignisse werden vom AdjustmentListener behandelt, und die zu implementierende Methode ist adjustmentValueChanged(). Sie können die getAdjustmentType()-Methode im AdjustmentEvent-Objekt benutzen, um genau festzustellen, wie die Bildlaufleiste geändert wurde.

Benutzen Sie addAdjustmentListener(), um den Adjustment-Listener in der Bildlaufleiste einzutragen.

Rollfelder (nur 1.1)

Rollfelder wurde in 1.1 neu aufgenommen. Dies geschah als Reaktion auf die Beschwerden, daß das Erstellen einer rollenden Oberfläche in 1.02 schwierig ist. (Sie müssen ein Panel und eine Bildlaufleiste miteinander verknüpfen – und das ist keine so einfache Aufgabe.) Ein Rollfeld ist einfach ein Container, der eine einzelne »Kind«-Komponente enthält. Das Rollfeld ist ein »Darstellungsfenster« der Kind-Komponente. Das bedeutet: wenn das Rollfeld kleiner ist als die Kind-Komponente, können Sie diese darin verschieben, um alle Teile der Komponente anzeigen zu lassen (siehe Abbildung 13.5). Alle Rollbewegungen werden vom AWT gehandhabt. Sie brauchen Rollereignisse nicht mehr wie in 1.02 zu verwalten.

siehe Abbildung

Abbildung 13.5:
Rollfelder

Rollfelder sind Instanzen der ScrollPane-Klasse. Um ein neues Rollfeld zu erstellen, benutzen Sie einen der folgenden Konstruktoren:

Nachdem Sie das ScrollPane-Objekt erstellt haben, wollen Sie dem Rollfeld vielleicht eine »Kind«-Komponente hinzufügen und dann das Rollfeld in das umgebende Panel einfügen. Beachten Sie, daß das Rollfeld nur jeweils eine »Kind«-Komponente enthalten kann.

ScrollPane scroller = new ScrollPane();

Panel surf = new Panel();
scroller.add(surf);
add(scroller);

Die ScrollPane-Klasse bietet mehrere Methoden zur Handhabung der Rollfelder. Diese Methoden sind in Tabelle 13.5 aufgeführt.

Tabelle 13.5: ScrollPane-Methoden

Methode

Aktion

getScrollPosition()

Gibt ein Point-Objekt zurück, das die Position innerhalb des Kindes angibt, das in der oberen linken Ecke des Feldes angezeigt wird

setScrollPosition(int, int)

Rollt das Panel an die angegebene Position im Kind

setScrollPosition(Point)

Rollt das Panel an die angegebene Point-Position im Kind

getHAdjustable()

Gibt ein Adjustable-Objekt zurück, das den Status der horizontalen Bildlaufleiste angibt

getVAdjustable()

Gibt ein Adjustable-Objekt zurück, das den Status der vertikalen Bildlaufleiste angibt

getViewportSize()

Gibt ein Dimension-Objekt zurück, das die Größe des Bildausschnitts des Panel mit Bildlaufleisten angibt

Der Status der Bildlaufleisten des Rollfelds wird, falls vorhanden, durch interne Objekte bestimmt, die die Adjustable-Schnittstellen implementieren. Um das Einheiten- oder Blockinkrement für die Bildlaufleisten zu ändern, benutzen Sie die Methoden getVAdjustable() und getHAdjustable(), um ein Adjustable-Objekt zu erhalten. Anschließend benutzen Sie die verschiedenen Adjustable-Methoden – getUnitIncrement(), getUnitIncrement(), getBlockIncrement(), setBlockIncrement() – zur Änderung des Verhaltens der Bildlaufleiste. (Bildlaufleisten implementiern auch die Adjustable-Schnittstelle, so daß diese Methoden bekannt sein sollten.)

Rollfelder können Bildlaufleistenereignisse über den AdjustmentListener und Container-Ereignisse (hinzugefügte oder gelöschte Komponente) über den ContainerListener erhalten. Außerdem können Sie Maus-, Tastatur- und Fokus-Ereignisse erhalten.

Zeichenbereiche (Canvas)

Sie können auf den meisten AWT-Komponenten, z.B. Panels, mit den Grafik-Methoden, die Sie während Tag 10, »Animationen, Bilder, Threads und Sounds«, kennengelernt haben, zeichnen. Zeichenbereiche können jedoch fast nichts anderes, als Sie zeichnen zu lassen. Sie können keine anderen Komponenten aufnehmen, aber Sie akzeptieren einfache Maus- und Tastaturereignisse, und Sie können eine Animation erstellen und Bilder in den Zeichenbereichen anzeigen. Wenn Sie ein Panel haben, dessen einzige Aufgabe es ist Bilder oder Animation anzuzeigen, wäre ein Zeichenbereich eine Oberfläche mit weniger Overhead als bei einem Panel der Fall ist.


Ein Zeichenbereich (Canvas) ist eine Komponenten, auf der gezeichnet werden kann.

Um einen Zeichenbereich zu erstellen, verwenden Sie die Canvas-Klasse und fügen sie genau wie andere Komponenten in ein Panel ein:

Canvas can = new Canvas();

add(can);

Cursor

Der Cursor ist das eigentliche Bild, das als Mauszeiger gezeigt wird – als Pfeil, Hand, Einfügemarke, Uhr oder sonstiges.

Cursor können, wie Ereignisse, stark variieren, je nach dem, ob Sie Java 1.02 oder 1.1 einsetzen. Allerdings sollte ich bei 1.02 nicht von Komponenten im Zusammenhang mit Cursorn reden, da sie keine Komponenten sind und ihr Einsatzbereich primär auf Java-Applikationen beschränkt ist, die in ihren eigenen Fenstern laufen. Bei 1.1 sind die Cursor wesentlich flexibler und leichter zu benutzen.

Mehr Informationen über den Einsatz von Cursorn in 1.02 finden Sie später in dieser Lektion im Abschnitt über Rahmen.

Bei 1.1 können Sie einer beliebigen Komponente einen Cursor hinzufügen und ihn jederzeit ändern. (Beachten Sie, daß einige Komponenten bereits ihren eigenen Standard-Cursor haben; Textfelder und Textbereiche z.B. ändern den Cursor zu einer Einfügemarke, wenn Sie sich darin befinden.)

Um einer Komponenten einen Cursor hinzuzufügen oder den Cursor der Komponente zu ändern, müssen Sie zunächst ein Cursor-Objekt erstellen. Der Cursor hat nur einen Konstruktor, Cursor(int), wobei das ganzzahlige Argument einer der vordefinierten Cursor-Typen ist. Die einsetzbaren Cursor-Typen sind in Tabelle 13.6 aufgeführt.

Tabelle 13.6: Cursor-Typen

Klassenvariable

Cursor

Cursor.CROSSHAIR_CURSOR

Kreuz-Cursor

Cursor.DEFAULT_CURSOR

Standard-Cursor (normalerweise ein Zeiger oder ein Pfeil)

Cursor.E_RESIZE_CURSOR

Ein Cursor, der anzeigt, daß etwas in seiner Größe verändert wird

Cursor.HAND_CURSOR

Hand-Cursor (um ein Objekt oder den Hintergrund zu verschieben)

Cursor.MOVE_CURSOR

Ein Cursor, der anzeigt, daß etwas verschoben wird

Cursor.N_RESIZE_CURSOR

Die Oberkante eines Fensters wird in der Größe verändert

Cursor.NE_RESIZE_CURSOR

Die obere rechte Ecke eines Fensters wird in der Größe verändert

Cursor.NW_RESIZE_CURSOR

Die obere linke Ecke eines Fensters wird in der Größe verändert

Cursor.S_RESIZE_CURSOR

Die Unterkante eines Fensters wird in der Größe verändert

Cursor.SE_RESIZE_CURSOR

Die untere rechte Ecke eines Fensters wird in der Größe verändert

Cursor.SW_RESIZE_CURSOR

Die untere linke Ecke eines Fensters wird in der Größe verändert

Cursor.TEXT_CURSOR

Texteingabe-Cursor (manchmal Einfügemarke genannt)

Cursor.W_RESIZE_CURSOR

Die linke Kante eines Fensters wird in der Größe verändert

Cursor.WAIT_CURSOR

Ein langer Vorgang findet statt (normalerweise ein Uhrensymbol oder eine Eieruhr)

Um den einen Cursor zu setzen wenden Sie mit dem Cursor-Objekt die setCursor()-Methode (definiert in Component, und als solche für alle AWT-Komponenten verfügbar) an:

Cursor cur = new Cursor(Cursor.HAND_CURSOR);

setCursor(cur);

Sie können auch die getCursor()-Komponentenmethode nutzen, um den aktuellen Cursor für eine Komponente festzustellen. The getCursor()-Methode gibt ein Cursor-Objekt zurück, und die Cursor-Methode getPredefinedCursor() gibt einen vordefinierten Cursor-Typ zurück.


Denken Sie daran, daß nicht alle Plattformen dieselben Cursor verwenden. Beispielsweise existieren auf dem Macintosh keine Cursor zur Änderung der Fenstergröße.

Spaß mit Komponenten

Die Component-Klasse ist die Wurzel aller AWT-Objekte: aller Elemente der Benutzeroberfläche, Panels, Zeichenbereiche und sogar Applets. Im AWT ist fast all das, was Sie anzeigen, ein Layout dafür erstellen, Farben ändern, darin zeichnen oder über Ereignisse damit interagieren können, eine Komponente sprich eine Instanz von Component oder einer Subklasse davon.

Komponenten verfügen über einen Satz von Methoden, mit denen Sie ihre Erscheinung oder ihr Verhalten änder können. Die Einsatzmöglichkeiten einiger dieser Methode haben Sie schon kennengelernt (setBackground(), setFont, size()), die speziell auf Applets anzuwenden sind. Aber die Methoden, die in der Component-Klasse definiert werden, können mit jeder beliebigen Komponente benutzt werden und somit können Sie das Erscheinungsbild oder das Verhalten nahezu jeden Elementes in Ihrem Programm ändern. Sogar individuell angepaßte Komponenten (Klassen, die von einem Panel oder Canvas erben) können Sie erstellen, um Ihre eigenen, speziellen AWT-Elemente in der Benutzeroberfläche zu erstellen.

In Tabelle 13.7 sind die Methoden, die Sie bei individuellen Komponenten nutzen können, aufgeführt. Mehr zu Methoden und Komponenten finden Sie in der Java API-Dokumentation zur Klasse Component.

Tabelle 13.7: Methoden für Komponenten

Methode

Was es bewirkt

getBackground()

Gibt ein Color-Objekt zurück, das die Hintergrundfarbe der Komponente angibt

setBackground(Color)

Setzt die Hintergrundfarbe der Komponente

getForeground()

Gibt ein Color-Objekt zurück, das die aktuelle Vordergrundfarbe der Komponente angibt

setForeground(Color)

Setzt die Vordergrundfarbe der Komponente

getFont()

Gibt ein Font-Objekt zurück, das den aktuellen Font der Komponente angibt

setFont(Font)

Ändert den aktuellen Font der Komponente

size()

Gibt ein Dimension-Objekt zurück, das die aktuelle Größe Komponente angibt. Sie könen dann die Höhe bzw. Breite mit size().width bzw. size().height bestimmen (nur bei 1.02; in 1.1 durch getSize() ersetzt)

getSize()

Nur bei 1.1; wie size()

minimumSize()

Die kleinste mögliche Größe der Komponente als ein Dimension-Objekt. minimumSize() wird normalerweise nur vom Layout-Manager genutzt, um festzulegen, wie klein er eine Komponente zeichnen kann; wenn Sie eine individuell angepaßte Komponente erstellen, überschreiben Sie diese Methode, um die Mindestgröße der Komponente zurückzugeben (nur bei 1.02; in 1.1 durc getMiniumSize() ersetzt)

getMinimumSize()

Nur bei 1.1; wie minimumSize()

preferredSize()

Die bevorzugte Größe der Komponente (normalerweise größer oder gleich minimumSize()) der Komponenten als ein Dimension-Objekt (nur bei 1.02; in 1.1 durch getPreferredSize() ersetzt)

getPreferredSize()

Nur bei 1.1; wie preferredSize()

resize(Dimension)

Ändert die Größe des Applet auf die aktuelle Größe. Bei individuell angepaßten Komponenten können Sie auch validate() nach der Größenänderung des Applet aufrufen, so daß das Layout neu aufgebaut werden kann (nur bei 1.02; in 1.1 durch setSize() ersetzt)

setSize()

Nur bei 1.1; wie resize()

inside(int, int)

Gibt true zurück, wenn die angegebenen X- und Y-Koordinaten innerhalb der Komponente liegen (nur bei 1.02; in 1.1 durch contains() ersetzt)

contains(int, int)

Nur bei 1.1; wie inside()

hide()

Versteckt die Komponente. Versteckte Komponenten werden nicht am Bildschirm angezeigt (nur bei 1.02; in 1.1 durch setVisible(false) ersetzt)

show()

Zeigt eine Komponente, die zuvor versteckt wurde (nur bei 1.02; in 1.1 durch setVisible(true) ersetzt)

setVisible(boolean)

Nur bei 1.1; ersetzt sowohl show()als auch hide()

isVisible()

Gibt true oder false zurück, abhängig davon, ob diese Komponente sichtbar ist (nicht versteckt)

disable()

Deaktiviert die Komponente – d.h., stoppt die Ereigniserzeugung. Deaktivierte Komponenten können nicht angeklickt werden, es kann nichts ausgewählt oder in sie geschrieben werden etc (nur bei 1.02; in 1.1 durch setEnabled(false)
ersetzt)

enable()

Aktiviert ein zuvor deaktiviertes Objekt (nur bei 1.02; in 1.1 durch setEnabled() ersetzt)

setEnabled(boolean)

Nur bei 1.1; ersetzt disable() und enable()

isEnabled()

Gibt true oder false zurück, abhängig davon, ob die Komponente aktiviert ist

Zusätzlich zu den speziellen Ereignissen für jede Komponente, die Sie schon während dieser und der vorigen Lektion kennengelernt haben, können alle Komponenten noch andere Ereignisse aufnehmen. Zunächst sind da Maus- und Tastaturereignisse, die Sie während Tag 12, »Erstellen von Benutzeroberflächen mit dem AWT«, gelernt haben. Und weiterhin bietet der ComponentListener eine Reihe von Ereignissen, die viele der Methoden in Tabelle 13.7 erzeugen: Komponenten verschieben, Größe der Komponenten verändern, Komponenten verstecken und Komponenten zeigen.

Bei Komponenten, die auch Container sind, werden die Container-Ereignisse für Komponente hinzugefügt bzw. Komponente gelöscht vom Container-Listener gehandhabt.

Komplettes Beispiel: RGB/HSB-Konverter

Wir haben bisher viel Theorie und kleine Beispiele durchgearbeitet und wenden uns nun einem größeren Beispiel zu, in dem die bisher gelernten Teile zusammengesetzt werden, wobei wir die Version 1.1 von Java einsetzen. Das folgende Beispiel-Applet zeigt die Erstellung von Layout, das Verschachteln von Panels, Erstellen von Komponenten der Benutzeroberfläche und die Ereignishandhabung sowie den Einsatz mehrerer Klassen, die in einem einzelnen Applet zusammengesetzt werden. Kurz gesagt: das ist das komplexeste Applet, das Sie bisher erstellt haben.

Abbildung 13.6 zeigt das Applet, das Sie in diesem Beispiel erstellen. Das ColorTest-Applet ermöglicht es Ihnen, Farben auf der Basis von RGB-Werten (Rot, Grün und Blau) und HSB-Werten (Farbton, Sättigung und Helligkeit) auszuwählen.

siehe Abbildung

Abbildung 13.6:
Das ColorTest-Applet


Schnell eine kurze Zusammenfassung zur Farbtheorie, für den Fall, daß Sie nicht damit vertraut sind: RGB definiert eine Farbe nach ihren Rot-, Grün- und Blau-Werten. Kombinationen dieser Werte können fast jede beliebige Farbe des Spektrums erzeugen. (Rot, Grün und Blau sind sogenannte additive Farben; so werden z.B. verschiedene Farben auf Ihrem Bildschirm und Fernseher dargestellt.)

HSB steht für Farbton, Sättigung und Helligkeit und bildet eine andere Art der Farbangabe. Der Farbton ist die eigentliche Farbe im Spektum (stellen Sie sich das als Wert auf einem Farbrad vor). Die Sättigung ist die Menge dieser Farbe: eine geringe Sättigung ergibt Pastellfarben, Farben mit hoher Sättigung sind lebendiger und »farbiger«. Helligkeit ist die Helligkeit bzw. Dunkelheit der Farbe. Keine Helligkeit ist Schwarz, volle Helligkeit ist Weiß.

Eine einzelne Farbe kann entweder durch ihre RGB-Werte oder durch ihre HSB-Werte dargestellt werden, und mathematische Algorithmen können eine Konvertierung zwischen beiden Werten vornehmen. Das ColorTest-Applet liefert einen grafischen Konverter zwischen diesen beiden.


Das ColorTest-Applet hat drei Hauptteile: eine Farbbox auf der linken Seite und zwei Gruppen mit Textfeldern auf der rechten Seite. Die erste Feldergruppe zeigt die RGB-Werte, die rechte die HSB-Werte an. Durch Ändern der Werte in einem der Textfelder wird die Farbbox aktualisiert, so daß jeweils die Farbe angezeigt wird, die in den Feldern gewählt wurde.

Dieses Applet nutzt zwei Klassen:

Arbeiten wir nun das Beispiel Schritt für Schritt durch, weil es kompliziert ist und leicht verwirren kann. Am Ende dieser Lektion steht der gesamte Code dieses Applet.


Obwohl ich bei der Erstellung dieses Applet die in 1.1 verwendeten Methodennamen und das 1.1-Ereignismodell verwendet habe, ist doch der überwiegende Teil des Codes dieses Applet auch für 1.02 identisch (alles außer der Ereignisverarbeitung). Wenn Sie noch mit 1.02 arbeiten, können Sie eine Version dieses Applet für 1.02 auf der CD finden, die mit dem Buch geliefert wird.

Entwerfen und Erstellen des Applet-Layouts

Die beste Art, mit einem Applet zu beginnen, das AWT-Komponenten beinhaltet, ist, sich zuerst um das Layout und dann um die Funktionalität zu kümmern. Beim Layout sollten Sie mit dem äußersten Panel beginnen und sich dann nach innen durcharbeiten.

Sie können sich die Arbeit vereinfachen, indem Sie alle Panels Ihres Benutzeroberflächendesigns zuerst auf Papier aufzeichnen. Das hilft Ihnen bei der Anordnung der Panels innerhalb des Applet oder Fensters und der besten Ausnutzung von Layout und Platz. Papierentwürfe sind selbst dann hilfreich, wenn Sie kein GridBagLayout verwenden, aber sicherlich nützlich, wenn Sie ein GridBagLayout nutzen. (Für dieses Applet nutzen Sie ein einfaches GridLayout.)

Abbildung 13.7 zeigt das ColorTest-Applet mit einem darüber gelegten Raster, so daß Sie eine Vorstellung davon bekommen können, wie die Panels und die eingebetteten Panels funktionieren.

siehe Abbildung

Abbildung 13.7:
Die Panels und Komponenten des ColorTest-Applet

Beginnen wir mit dem äußersten Panel – dem Applet selbst. Dieses Panel besteht aus drei Teilen: der Farbbox auf der linken Seite, den RGB-Textfeldern in der Mitte und den HSB-Feldern auf der rechten Seite.

Da das äußerste Panel das Applet selbst ist, ist die ColorTest-Klasse die Klasse des Applet. Sie wird von Applet abgeleitet. Außerdem importieren Sie hier die AWT-Klassen. (Beachten Sie, daß wir der Einfachheit halber das komplette Paket importieren, da wir hier soviel davon nutzen.)

import java.awt.*;


public class ColorTest extends java.applet.Applet {
...
}

Dies Applet hat drei Hauptelemente, die verfolgt werden müssen: die Farbbox und die beiden untergeordneten Panels. Jedes dieser beiden Sub-Panels bezieht sich auf etwas anderes, aber grundsätzlich sind sie gleich und verhalten sich gleich. Anstatt hier viel Code in diese Klasse zu kopieren können Sie die Gelegenheit nutzen und eine weitere Klasse für diese Sub-Panels erstellen, wobei Sie Instanzen dieser Klasse hier im Applet verwenden und alles miteinader über Methoden kommunizieren lassen. Schon bald definieren wir die neue Klasse namens ColorControls.

Jetzt müssen Sie jedoch erst wissen, wie Sie alle drei Teile des Applet handhaben, damit Sie sie bei Änderungen aktualisieren können. Erstellen wir also drei Instanzenvariablen: eine vom Typ Canvas für die Farbbox und die anderen beiden vom Typ ColorControls für die Kontroll-Panels:

ColorControls RGBcontrols, HSBcontrols;

Canvas swatch;

Jetzt können Sie mit der init()-Methode fortfahren, in der die gesamte Initialisierung und das Layout stattfinden. Sie arbeiten in drei Schritten:

1. Erstellen Sie das Layout für die großen Teile des Panel. Ein FlowLayout wäre zwar möglich, jedoch eignet sich ein GridLayout mit einer Zeile und drei Spalten besser.

Der erste Schritt ist das Layout. Benutzen Sie ein GridLayout mit einem Abstand von 10 Punkten, um die Komponenten voneinander zu trennen:

setLayout(new GridLayout(1, 3, 5, 10));

Der zweite Schritt ist das Erstellen der Komponenten – zuerst den Zeichenbereich. Sie haben eine Instanzvariable, die diesen enthält. Jetzt erstellen Sie den Zeichenbereich und initialisieren den Hintergrund mit Schwarz:

swatch = new Canvas();

swatch.setBackground(Color.black);

Sie müssen hier auch zwei Instanzen des noch nicht existierenden ColorControls-Panel einfügen. Da Sie diese Klasse noch nicht angelegt haben, wissen Sie nicht, wie die Konstruktoren für diese Klasse aussehen werden. In diesem Fall fügen Sie hier einfach ein paar Platzhalter-Konstruktoren ein und füllen die Einzelheiten später aus.

RGBcontrols = new ColorControls(...)

HSBcontrols = new ColorControls(...);

Der dritte Schritt ist das Einfügen aller drei Komponenten in das Applet-Panel, und zwar so:

add(swatch);

add(RGBcontrols);
add(HSBcontrols);

Da Sie gerade am Layout arbeiten, fügen Sie als zusätzlichen Freiraum einen Eckeinsatz ein – 10 Punkt an allen Kanten:

public Insets getInsets() {

return new Insets(10, 10, 10, 10);
}

Soweit mitgekommen? Dann müßten Sie jetzt drei Instanzenvariablen, eine init()-Methode mit zwei unvollständigen Konstruktoren und eine getInsets()-Methode in Ihrer ColorTest-Klasse haben. Fahren wir nun fort mit der Erstellung des Layouts für die Subpanels in der ColorControls-Klasse, damit Sie die Konstruktoren angeben und das Layout abschließen können.

Definieren der untergeordneten Panels

Die ColorControls-Klasse enthält das Verhalten für das Layout und die Handhabung der Sub-Panels, die die RGB- und HSB-Farbwerte darstellen. ColorControls braucht keine Subklasse von Applet zu sein, da es sich nicht um ein Applet sondern ein Panel handelt. Definieren Sie es so, daß es eine Subklasse von Panel ist:

import java.awt.*


class ColorControls extends Panel {
...
}

Die ColorControls-Klasse benötigt eine Reihe von Instanzvariabeln, damit die Informationen vom Panel zurück zum Applet gelangen können. Die erste dieser Instanzvariablen ist eine Rückverzweigung zur Klasse des Applet, die dieses Panel enthält. Da die äußere Applet-Klasse die Aktualisierung der einzelnen Panels steuert, muß dieses Panel eine Möglichkeit haben, dem Applet mitzuteilen, daß sich etwas geändert hat. Und um eine Methode in dem Applet aufzurufen, müssen Sie einen Bezug zum Objekt herstellen. Also ist die erste Instanzenvariable eine Referenz zu einer Instanz der Klasse ColorTest:

ColorTest applet;

Stellen Sie sich vor, daß die Applet-Klasse alles aktualisiert, dann ist diese Klasse an den einzelnen Textfeldern in diesem Sub-Panel interessiert. Sie müssen also für jedes dieser Textfelder eine Instanzvariable erstellen:

TextField tfield1, tfield2, tfield3;

Jetzt können Sie sich an den Konstruktor für diese Klasse machen. Da diese Klasse kein Applet ist, verwenden Sie zur Initialisierung nicht init() sondern eine Konstruktor-Methode. Innerhalb des Konstruktors machen Sie das, was Sie in init() machen würden: das Layout für das untergeordnete Panel sowie die Textfelder erstellen und Sie in das Panel einfügen.

Das Ziel ist hier, die ColorControls-Klasse allgemein genug zu halten, so daß Sie sie sowohl für die RGB- als auch für die HSB-Felder nutzen können. Diese beiden Panels unterscheiden sich nur durch die Beschriftung für den Text. Das sind die drei Werte, die Sie haben müssen, bevor Sie das Objekt erzeugen können. Sie können diese drei Werte über die Konstruktoren in ColorTest übergeben. Aber Sie brauchen noch mehr: den Bezug zum übergeordneten Applet, den Sie ebenfalls über den Konstruktor erhalten können.

Damit haben Sie jetzt vier Argumente für den grundlegenden Konstruktor der ColorControls-Klasse. Die Signatur für den Konstruktor sieht so aus:

ColorControls(ColorTest parent,

String l1, String l2, String l3) {
}

Lassen Sie uns die Definition dieses Konstruktors beginnen, indem Sie zunächst der applet-Instanzvariable den Wert von parent zuweisen:

applet = parent;

Als nächstes erstellen Sie das Layout für dieses Panel. Sie können hier auch wieder, wie beim Applet-Panel, ein GridLayout für diese Sub-Panels verwenden, aber diesmal hat das Raster drei Zeilen (je eine für jedes Textfeld mit Beschriftung) und zwei Spalten (eine für die Beschriftung und eine für die Felder). Und definieren Sie weider einen Abstand von 10 Punkten zwischen den Komponenten im Raster:

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

Jetzt können Sie die Komponenten erstellen und in das Panel einfügen. Zuerst erstellen Sie die Textfeld-Objekte (initialisiert mit der Zeichenkette "0") und weisen sie den entsprechenden Instanzenvariablen zu:

tfield1 = new TextField("0");

tfield2 = new TextField("0");
tfield3 = new TextField("0");

Dann fügen Sie diese Felder und die zugehörigen Label in das Panel ein, wobei Sie die verbleibenden Parameter im Konstruktor als Beschriftungstext in den Labels verwenden:

add(new Label(l1, Label.RIGHT));

add(tfield1);
add(new Label(l2, Label.RIGHT));
add(tfield2);
add(new Label(l3, Label.RIGHT));
add(tfield3);

Damit ist der Konstruktor für die Sub-Panel-Klasse ColorControls abgeschlossen. Sind Sie mit dem Layout fertig? Noch nicht ganz. Fügen Sie noch einen Eckeinsatz im Sub-Panel ein – nur an der oberen und unteren Kante – das sieht besser aus. Die Eckeinsätze fügen Sie genauso ein, wie Sie es schon bei der ColorTest-Klasse getan haben, nämlich mit der getInsets()-Methode:

public Insets getInsets() {

return new Insets(10, 10, 0, 0);
}

Sie haben es fast geschafft. 98% der Grundstruktur sind fertig, und es ist nur noch ein Schritt auszuführen. Dazu gehen Sie zurück zu ColorTest und ergänzen die Platzhalter-Konstruktoren für das Sub-Panel, damit sie mit dem tatsächlichen Konstruktor für ColorControls übereinstimmen.

Der Konstruktor für ColorControls, den Sie gerade erstellt haben, hat vier Argumente: das ColorTest-Objekt und drei Labels (Zeichenketten). Erinnern Sie sich noch, wie Sie die init()-Methode für ColorTest erstellt haben? Sie haben zwei Platzhalter für das Erstellen neuer ColorControls-Objekte eingefügt. Diese Platzhalter ersetzen Sie jetzt mit der richtigen Version. Und vergessen Sie dabei nicht, die vier Argumente einzufügen, die der Konstruktor braucht: das ColorTest-Objekt und die drei Zeichenketten. Das ColorTest-Objekt können Sie an diese Konstruktor-Aufrufe mit dem Schlüsselwort this übergeben:

RGBcontrols = new ColorControls(this, "Red", "Green", "Blue");

HSBcontrols = new ColorControls(this, "Hue", "Saturation", "Brightness");


Bei allen Intialisierungswerten in diesem Beispiel habe ich die Zahl 0 gewählt (eigentlich die Zeichenkette "0"). Für die Farbe Schwarz ist sowohl der RGB- aus auch der HSB-Wert 0, und daher habe ich diese Vorgabe gewählt. Wenn Sie das Applet mit einer anderen Farbe initialisieren wollen, können Sie die ColorControls-Klasse so umschreiben, daß sie auch Initialisierungswerte nutzt, um die Label zu initialisieren. Dies hier war nur ein kürzeres Beispiel.

Ereignisbehandlung

Mit dem fertigen Layout können Sie die Ereignishandhabung und die Aktualisierungsvorgänge zwischen den verschiedenen Komponenten festlegen, so daß das Applet reagieren kann, wenn der Benutzer mit dem Applet interagiert.

Die Felder erzeugen mehrere unterschiedliche Arten von Ereignissen, und Sie müssen sicherstellen, daß jedes Ereignis bedacht wird. Insbesondere erzeugen Textfelder Aktionsereignisse, wenn der Benutzer eine Zahl eingibt und die Zeilenschaltung betätigt. Gibt der Benutzer aber eine Zahl ein und drückt die Tabulator-Taste, um zum nächsten Feld zu springen, oder er geht mit der Maus zum nächsten Feld über, erzeugt keine dieser Bewegungen ein Aktionsereignis. Jedoch erzeugen Sie ein Fokusverlustereignisse, wenn der Eingabefokus auf eine andere Komponente verschoben wird. In diesem Applet behandeln Sie sowohl Aktions- als auch Fokusverlustereignisse.

Die übergeordnete ColorTest-Klasse ist eigentlich für die Aktualisierung zuständig, da Sie alle Sub-Panels nachverfolgt. Da das eigentliche Ereignis im Sub-Panel und dessen Komponenten stattfindet, müssen Sie jedoch die Ereignis-Listeners in Code für das Sub-Panel eintragen.

Wie bei allen Listeners können Sie entweder eine neue Klasse für den Listener anlegen oder das Verhalten des Listener in die aktuelle Klasse einbinden. Das Erstellen neuer Klassen entspräche zwar eher der objektorientierten Programmierung, aber der Einfachheit halber können Sie statt dessen die Listeners in ColorControls einfügen.

Sie müssen zwei verschiedene Listeners einfügen: ActionListener für die Aktionsereignisse und FocusListener für die Fokusereignisse. Diese Listener-Benutzeroberflächen fügen Sie in die Klassendefintion ein:

class ColorControls extends Panel

implements FocusListener, ActionListener {

Als nächstes fügen Sie Stub-Methoden für die Benutzeroberfläche im Hauptteil der Klasse ein. ActionListener hat nur eine actionPerformed()-Methode, FocusListener hat jedoch zwei: focusGained() und focusLost(). Nachstehend die Methodensignaturen:

public void focusGained(FocusEvent e) { }

public void focusLost(FocusEvent e) { }
public void actionPerformed(ActionEvent e) { }

Die Methoden focusGained() und actionPerformed() sind die beiden, die für Sie interessant sind. Aber was machen sie? Das äußere Applet (ColorTest) ist dafür zuständig, alles zu aktualisieren, so daß Sie hier wirklich nur dem äußeren Applet mitteilen müssen, daß sich etwas geändert hat (und die Referenz zum aktuellen Panel übergeben müssen, damit das Applet alle Werte erhalten kann). Um es Ihnen leichter zu machen – und weil ich das Applet schon geschrieben habe und weiß, was passieren wird – können Sie auch die Methode update() im äußeren Applet aufrufen. Sie können sie aus jeder der Methoden focusLost() und action-Performed() aufrufen. Dazu wird ein Argument, this, für das aktuelle Panel benötigt, damit das äußere Applet herausfinden kann, was sich geändert hat. Die Listener-Methoden, mit denen update() aufgerufen wird:

public void focusLost(FocusEvent e) {

applet.update(this);
}

public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof TextField)
applet.update(this);
}

Beachten Sie den einen Test innerhalb von actionPerformed() um sicherzustellen, daß Sie es mit einem Textfeld zu tun haben. Das Testen ist nicht unbedingt nötig da keine anderen Aktionsereignisse in diesem Applet erzeugt werden können, jedoch ist das Testen immer eine gute Idee.

Sieht das zu einfach aus? Es ist wirklich einfach. Im nächsten Abschnitt werde ich die update()-Methode definieren, und Sie werden sich wünschen, wir hätten hier aufgehört.

Vergessen Sie nicht den letzten Schritt: das Eintragen der Listeners. Hier haben Sie sowohl Fokus- als auch Aktion-Listeners, und Sie müsen Sie für alle drei Textfelder des Panel eintragen. Das geschieht über einge Zeilen in der init()-Methode in ColorControls:

add(new Label(l1, Label.RIGHT));

tfield1.addFocusListener(this);
tfield1.addActionListener(this);
add(tfield1);
add(new Label(l2, Label.RIGHT));
tfield2.addFocusListener(this);
tfield2.addActionListener(this);
add(tfield2);
tfield3.addFocusListener(this);
tfield3.addActionListener(this);
add(new Label(l3, Label.RIGHT));
add(tfield3);

Aktualisieren des Ergebnisses

Jetzt kommt der schwierige Teil: die eigentliche Aktualisierung auf der Basis der neuen Werte, gleichgültig welches Textfeld geändert wurde. Bei diesem Schritt definieren Sie die update()-Methode in der ColorTest-Klasse. Diese update()-Methode nimmt ein einzelnes Argument an: die ColorControls-Instanz, die den geänderten Wert enthält. (Sie erhalten das Argument aus den Ereignismethoden im ColorControls-Objekt.)


Fürchten Sie nicht, daß diese update()-Methode die update()-Methode des Systems stört? Sie tut es nicht, denn wie Sie sich erinnern, können Methoden den gleichen Namen, jedoch unterschiedliche Signaturen und Definitionen haben. Da diese update()-Methode nur ein Argument vom Typ ColorControls hat, beeinflußt sie nicht die übrigen update()-Versionen. Normalerweise sollten alle Methoden namens update() im Grunde dieselbe Aufgabe haben. Das ist hier nicht der Fall, aber das ist nur ein Beispiel.

Die update()-Methode ist für das Aktualisieren aller Panels im Applet zuständig. Um zu wissen, welches Panel zu aktualisieren ist, müssen Sie wissen, welches Panel geändert wurde. Das finden Sie heraus, indem Sie testen, ob das abgegebene Argument mit den untergeordneten Panels, die Sie in den Instanzvariablen RGBcontrols und HSBcontrols gespeichert haben, identisch ist:

void update(ColorControls controlPanel) {


if (controlPanel == RGBcontrols) { // RGB geändert, HSB aktualisieren
...
} else { // HSB geändert, RGB aktualisieren
...
}
}

Dieser Test ist der Kern der update()-Methode. Beginnen wir mit dem ersten Fall – einer Zahl, die in den RGB-Textfeldern geändert wurde. Anhand dieser neuen RGB-Werte müssen Sie ein neues Color-Objekt erstellen und die Werte im HSB-Panel aktualisieren. Damit Sie nicht so viel tippen müssen, können Sie ein paar lokale Variablen deklarieren, in die Sie einige Grundwerte stellen. Besonders die Werte der Textfelder sind Zeichenketten, deren Werte Sie dazu heranziehen können, die getText()-Methode zu nutzen, die in den TextField-Objekten des ColorControls-Objekts definiert wurden. Da Sie bei dieser Methode meistens mit Ganzzahlwerten arbeiten, können Sie diese Zeichenketten abfragen, sie in Ganzzahlen konvertieren und in lokalen Variablen speichern (value1, value2, value3). Mit dem folgenden Code erledigen Sie diese Aufgabe (das sieht komplizierter aus als es ist):

int value1 = Integer.parseInt(controlPanel.tfield1.getText());

int value2 = Integer.parseInt(controlPanel.tfield2.getText());
int value3 = Integer.parseInt(controlPanel.tfield3.getText());

Wenn Sie die lokalen Variablen definieren, brauchen Sie auch eine für das neue Color-Objekt:

Color c;

Nehmen wir an, eines der Textfelder auf der RGB-Seite des Applet hat sich geändert, und Sie fügen den Code in den if-Teil der update()-Methode. Sie müssen ein neues Color-Objekt erstellen und die HSB-Seite des Panel aktualisieren. Dieser erste Teil ist einfach. Bei drei RGB-Werten, können Sie ein neues Color-Objekt erstellen, wobei Sie diese Werte als Argumente für den Konstruktor nutzen:

c = new Color(value1, value2, value3);


Dieser Teil des Beispiels ist nicht sehr robust. Er basiert auf der Annahme, daß der Benutzer nichts anderes als ganze Zahlen zwischen 0 und 255 in die Textfelder eingibt. Eine bessere Version wäre ein Test, mit dem sichergestellt wird, daß keine Dateneingabefehler passieren. Aber ich wollte das Beispiel kurzhalten.

Jetzt konvertieren Sie die RGB-Werte nach HSB. Standardalgorithmen können eine auf RGB basierende Farbe in eine HSB-Farbe konvertieren, aber Sie brauchen Sie nicht nachzusehen. Die Color-Klasse verfügt über eine Klassenmethode namens RGBtoHSB(), die Sie benutzen können. Diese Methode erledigt die Arbeit für Sie – oder zumindest die meiste Arbeit. Die RGBtoHSB()-Methode wirft jedoch zwei Probleme auf:

Aber keines dieser Probleme ist unüberwindbar. Sie müssen lediglich einige Zeilen Code hinzufügen. Beginnen wir mit dem Aufruf von RGBtoHSB() mit den neuen RGB-Werten. Die Methode gibt ein Array mit floats zurück, und daher müssen Sie eine lokale Variable (HSB) erstellen, die die Ergebnisse der RBGtoHSB()-Methode speichert. (Denken Sie daran, daß Sie auch ein leeres float-Array als viertes Argument für RGBtoHSB() erstellen und übergeben müssen.)

float[] HSB = Color.RGBtoHSB(value1, value2, value3, (new float[3]));

Jetzt konviertieren Sie diese Fließkommawerte, die zwischen 0.0 und 1.0 liegen, in Werte zwischen 0 und 100 (für Sättigung und Helligkeit) bzw. 0 und 360 für den Farbton, indem Sie die entsprechenden Zahlen multiplizieren und die Werte dem array wieder zuweisen:

HSB[0] *= 360;

HSB[1] *= 100;
HSB[2] *= 100;

Jetzt haben Sie alle gewünschten Zahlen. Der letzte Teil der Aktualisierung ist, diese Werte wieder in die Textfelder einzutragen. Natürlich sind diese Werte noch immer Fließkommazahlen, und Sie müssen sie in int-Werte casten, bevor Sie in Zeichenketten umgewandelt und gespeichert werden:

HSBcontrols.tfield1.setText(String.valueOf((int)HSB[0]));

HSBcontrols.tfield2.setText(String.valueOf((int)HSB[1]));
HSBcontrols.tfield3.setText(String.valueOf((int)HSB[2]));

Die Hälfte haben Sie geschafft. Der nächste Teil des Applet ist derjenige, der die RGB-Werte aktualisiert, wenn sich ein Textfeld auf der HSB-Seite geändert hat. Das geschieht im else-Teil des großen if...else-Abschnitts, wo diese Methode definiert und festgelegt wird, was aktualisiert wird.

Es ist eigentlich einfacher, RGB-Werte aus HSB-Werten zu erzeugen, als andersherum. Eine Klassenmethode in der Color-Klasse, die getHSBColor()-Methode, erzeugt ein neues Color-Objekt aus den drei HSB-Werten. Wenn Sie ein Color-Objekt haben, können Sie die RGB-Werte daraus leicht herausziehen. Der Trick ist natürlich, daß getHSBColor drei Fließkommaargumente annimmt, wohingegen die Werte, die Sie haben, Ganzzahlwerte sind, mit denen ich lieber arbeite. Bei diesem getHSBColor-Aufruf müssen Sie jetzt die Ganzzahlwerte aus den Textfeldern in floats casten und sie durch den entsprechenden Konvertierungsfaktor dividieren. Das Ergebnis von getHSBColor ist ein Color-Objekt, und deshalb können Sie das Objekt einfach der lokalen Variablen c zuweisen, damit Sie es später weiterverwenden können:

c = Color.getHSBColor((float)value1 / 360,

(float)value2 / 100, (float)value3 / 100);

Sind alle Elemente des Color-Objekts gesetzt, müssen die RGB-Werte für das Aktualisieren aus dem Color-Objekt extrahiert werden. Diese Arbeit erledigen für Sie die Methoden getRed(), getGreen() und getBlue(), die in der Color-Klasse definiert sind:

RGBcontrols.tfield1.setText(String.valueOf(c.getRed()));

RGBcontrols.tfield2.setText(String.valueOf(c.getGreen()));
RGBcontrols.tfield3.setText(String.valueOf(c.getBlue()));

Schließlich müssen Sie noch, unabhängig davon ob sich der RGB- oder HSB-Wert geändert hat, die Farbbox auf der linken Seite aktualisieren, damit sie die neue Farbe darstellt. Da das neue Color-Objekt in der Variablen c gespeichert ist, können Sie die setBackground-Methode zu Änderung der Farbe nutzen. Denken Sie aber daran, daß setBackground den Bildschirm nicht automatisch neu zeichnet, so daß Sie auch ein repaint() aufrufen müssen:

swatch.setBackground(c);

swatch.repaint();

Das war's! Jetzt kompilieren Sie die beiden Klassen ColorTest und ColorControls, erstellen eine HTML-Datei, um das ColorTest-Applet zu laden, und probieren es aus.

Der komplette Quellcode

Listing 13.1 enthält den vollständigen Quellcode für die Applet-Klasse ColorTest, und Listing 13.2 zeigt den Quellcode für die Handler-Klasse ColorControls. Meist ist es anhand eines kompletten Quelltextes einfacher, sich vorzustellen, was in einem Applet abläuft – wenn alles zusammensteht und Sie die Methodenaufrufe nachvollziehen und sehen können, welche Werte übergeben werden. Beginnen wir mit der init()-Methode im ColorTest-Applet.

Listing 13.1: Das ColorTest-Applet

 1:  import java.awt.*;

2:
3: public class ColorTest extends java.applet.Applet {
4: ColorControls RGBcontrols, HSBcontrols;
5: Canvas swatch;
6:
7: public void init() {
8: setLayout(new GridLayout(1,3,5,15));
9:
10: // Farb-Swatch
11: swatch = new Canvas();
12: swatch.setBackground(Color.black);
13:
14: // Subpanels für die Steuerungen
15: RGBcontrols = new ColorControls(this, "Red", "Green", "Blue");
16: HSBcontrols = new ColorControls(this, "Hue", "Saturation", "Brightness");
17:
18: // alles ins Layout einfügen
19: add(swatch);
20: add(RGBcontrols);
21: add(HSBcontrols);
22: }
23:
24: public Insets getInsets() {
25: return new Insets(10,10,10,10);
26: }
27:
28: void update(ColorControls controlPanel) {
29: Color c;
30: // Zeichenkettenwerte aus Textfeldern holen und in Ganzzahlen konvertieren
31: int value1 = Integer.parseInt(controlPanel.tfield1.getText());
32: int value2 = Integer.parseInt(controlPanel.tfield2.getText());
33: int value3 = Integer.parseInt(controlPanel.tfield3.getText());
34:
35: if (controlPanel == RGBcontrols) { // RGB geändert, HSB aktualisieren
36: c = new Color(value1, value2, value3);
37:
38: // RGB-Werte in HSB-Werte konvertieren
39: float[] HSB = Color.RGBtoHSB(value1, value2, value3, (new float[3]));
40: HSB[0] *= 360;
41: HSB[1] *= 100;
42: HSB[2] *= 100;
43:
44: // HSB-Felder zurücksetzen
45: HSBcontrols.tfield1.setText(String.valueOf((int)HSB[0]));
46: HSBcontrols.tfield2.setText(String.valueOf((int)HSB[1]));
47: HSBcontrols.tfield3.setText(String.valueOf((int)HSB[2]));
48:
49: } else { // HSB geändert, RGB aktualisieren
50: c = Color.getHSBColor((float)value1 / 360,
51: (float)value2 / 100, (float)value3 / 100);
52:
53: // RGB-Felder zurücksetzen
54: RGBcontrols.tfield1.setText(String.valueOf(c.getRed()));
55: RGBcontrols.tfield2.setText(String.valueOf(c.getGreen()));
56: RGBcontrols.tfield3.setText(String.valueOf(c.getBlue()));
57: }
58:
59: // Swatch aktualisieren
60: swatch.setBackground(c);
61: swatch.repaint();
62:}
63:}

Listing 13.2: Die ColorControls-Klasse

 1: import java.awt.*;

2: import java.awt.event.*;
3:
4: class ColorControls extends Panel implements FocusListener, ActionListener {
5: TextField tfield1, tfield2, tfield3;
6: ColorTest applet;
7:
8: ColorControls(ColorTest parent,
9: String l1, String l2, String l3) {
10:
11: // die Referenz auf das äußere Applet zuweisen
12: applet = parent;
13:
14: // das Layout erstellen
15: setLayout(new GridLayout(3,2,10,10));
16:
17: tfield1 = new TextField("0");
18: tfield2 = new TextField("0");
19: tfield3 = new TextField("0");
20:
21: add(new Label(l1, Label.RIGHT));
22: tfield1.addFocusListener(this);
23: tfield1.addActionListener(this);
24: add(tfield1);
25: add(new Label(l2, Label.RIGHT));
26: tfield2.addFocusListener(this);
27: tfield2.addActionListener(this);
28: add(tfield2);
29: tfield3.addFocusListener(this);
30: tfield3.addActionListener(this);
31: add(new Label(l3, Label.RIGHT));
32: add(tfield3);
33: }
34:
35: public Insets getInsets() {
36: return new Insets(10,10,0,0);
37: }
38:
39: public void focusGained(FocusEvent e) {}
40: public void focusLost(FocusEvent e) {
41: applet.update(this);
42: }

43: public void actionPerformed(ActionEvent e) {
44: if (e.getSource() instanceof TextField)
45: applet.update(this);
46: }
47:}

Fenster, Rahmen und Dialogfelder

Zusätzlich zu den Grafiken, Ereignissen, Komponenten der Benutzeroberfläche und Layoutmechanismen bietet AWT Möglichkeiten zur Erstellung von Elementen der Benutzeroberfläche außerhalb eines Applet oder Browser: Fenster, Menüs und Dialogfelder. Damit können Sie ausgewachsene Anwendungen als Teil Ihres Applet oder als unabhängige Java-Applikationen erstellen.

Die AWT-Fensterklassen

Die Java AWT-Klassen zur Erstellung von Fenstern und Dialogfeldern erben von einer einzigen Klasse: Window. Die Window-Klasse, die selbst wiederum von Container erbt (und daher eine standardmäßige AWT-Komponente ist), liefert das allgemeine Verhalten für alle fensterähnlichen Elemente. Im allgemeinen verwenden Sie keine Instanzen von Window, jedoch setzen Sie Instanzen von Frame oder Dialog ein. Abbildung 13.8 zeigt die einfache Hierarchie der Window-Klasse.

siehe Abbildung

Abbildung 13.8:
Die Hierarchie der Window-Klasse

Die Frame-Klasse bietet ein Fenster mit einer Titelleiste, einer Schließen-Schaltfläche und anderen, plattformabhängigen Fenstermerkmalen. In diesen Rahmen können Sie auch Menüleisten einfügen. Dialog hingegen ist eine eingeschränktere Form von Frame, die normalerweise nicht über einen Titel verfügt. FileDialog, eine Subklasse von Dialog, bietet ein standard Dateiauswahldialogfeld (das normalerweise wegen der Sicherheitsbeschränkungen nur innerhalb von Java-Applikationen genutzt werden kann).

Wenn Sie ein neues Fenster oder Dialogfeld in Ihr Applet oder Ihre Applikation aufnehmen wollen, erstellen Sie Subklassen zu den Klassen Frame und Dialog.

Rahmen

Rahmen sind Fenster, die von dem Applet oder Browser unabhängig sind, der das Applet enthält. Es handelt sich hierbei um eigenständige Fenster mit eigenen Titeln, Schaltflächen zum Verkleinern, Vergrößern und Schließen sowie Menüleisten. Sie können Rahmen für Ihre eigenen Applets erstellen, um Fenster zu erzeugen, oder Rahmen in Java-Applikationen zu nutzen, um den Inhalt dieser Applikation aufzunehmen.


Ein Rahmen ist ein plattformspezifisches Fenster mit einem Titel, einer Menüleiste, Schaltflächen zum Verkleinern, Vergrößern und Schließen sowie anderen Fensterfunktionen.

Um einen Rahmen zu erstellen, benutzen Sie einen der folgenden Konstruktoren:

Da Frame von Window abgeleitet ist, diese wiederum von Container und jene wiederum von Component, werden Rahmen grundsätzlich so wie andere AWT-Komponenten erstellt und eingesetzt. Rahmen sind Container wie Panels, so daß Sie andere Komponenten mit der add()-Methode einfügen können. Das Standardlayout für Rahmen ist BorderLayout. Hier ein Beispiel für die Erstellung eines Rahmens, das Festlegen des Layouts und Hinzufügen von zwei Schaltflächen:

win = new Frame("My Cool Window");

win.setLayout(new BorderLayout(10, 20));
win.add("North", new Button("Start"));
win.add("Center", new Button("Move"));

Um die Größe für einen neuen Rahmen festzulegen, verwenden Sie die resize()-Methode mit der Breite und Höhe des neuen Fensters. Diese Codezeile beispielsweise ändert die Fenstergröße auf 100 Pixel Breite und 200 Pixel Höhe:

win.resize(100, 200);

Beachten Sie jedoch folgendes: Da unterschiedliche System eine unterschiedliche Vorstellung davon haben, was ein Pixel ist und diese Pixel in unterschiedlicher Auflösung darstellen, ist es schwierig ein Fenster zu erstellen, daß die »richtig« Größe bei jeder Plattform hat. Fenster, die auf einer Plattform in Ordnung sind, können auf einer anderen viel zu groß oder viel zu klein sein. Ein Ausweg aus diesem Problem ist, die Methode pack() anstatt resize() zu verwenden. Die pack()-Methode, die keine Argumente hat, erstellt ein Fenster in kleinstmöglicher Größe auf der Basis der aktuellen Größe der Komponenten im Fenster sowie des eingesetzten Layout-Managers und der eingesetzten Eckeinsätze. Im folgenden Beispiel werden zwei Schaltflächen erstellt und in ein Fester eingefügt. Das Fenster wird dann auf die kleinstmögliche Größe reduziert, die diese Schaltflächen enthalten kann.

win = new Frame("My Other Cool Window");

win.setLayout(new FlowLayout()));
win.add("North", new Button("OK"));
win.add("Center", new Button("Cancel"));
win.pack();

Ein neu erstelltes Fenster ist zunächst unsichtbar. Sie müssen die show()-Methode anwenden, um dieses Fenster am Bildschirm anzeigen zu lassen (und Sie können es mit hide() wieder verbergen):

win.show();

Wenn Sie Pop-up-Fenster aus einem Applet heraus aktivieren, kann der Browser auf irgendeine Art deutlich machen, daß das Fenster kein reguläres Browser-Fenster ist – normalerweise erscheint dann eine Warnung im Fenster selbst. In Net-scape sagt eine gelbe Leiste unten in jedem Fenster Untrusted Java Window. Diese Warnung soll den Benutzer informieren, daß das Fenster aus einem Applet kommt und nicht vom Browser selbst. (Denken Sie daran, daß die Frame-Klasse Fenster erzeugt, die genauso aussehen wie ein normales Fenster.) Die soll davor bewahren, ein schlechtes Applet zu erstellen, das den Benutzer beispielsweise zur Eingabe des Paßworts auffordert. Sie können nichts gegen diese Warnung unternehmen – sie bleibt immer, wenn Sie Fenster mit Applets zusammen benutzen.

Die Listings 13.3, 13.4 und 13.5 zeigen die Klassen, die ein einfaches Applet mit einem Pop-up-Fenster erzeugen. Sowohl das Applet als auch das Fenster sind in Abbildung 13.9 dargestellt. Das Applet verfügt über zwei Schaltflächen: eine zum Anzeigen und eine zum Verbergen des Fensters. Der Fensterrahmen selbst, der aus einer Subklasse erstellt wird, die ich BaseFrame1 genannt habe, enthät ein Label: This is a Window. Die PopupActions-Klasse verwaltet schließlich die Ereignisse für die Schaltflächen im 1.1-Ereignismodell. Ich werde mich weiterhin auf dieses Fenster und dieses Applet beziehen, damit Sie besser verstehen, was passiert und es später für Sie leichter wird.


Bei diesem Beispiel habe ich das 1.1-Ereignismodell eingesetzt. Wenn Sie mit 1.02 arbeiten, finden Sie eine ältere Version dieses Applet mit dem 1.02-Ereignismodell auf der CD zu diesem Buch.

Listing 13.3: Ein Pop-up-Fenster

 1: import java.awt.*;

2:
3: public class PopupWindow extends java.applet.Applet {
4: Frame window;
5: Button open, close;
6:
7: public void init() {
8: PopupActions handlebutton = new PopupActions(this);
9:
10: open = new Button("Open Window");
11: open.addActionListener(handlebutton);
12: add(open);
13:
14: close = new Button("Close Window");
15: close.addActionListener(handlebutton);
16: add(close);
17:
18: window = new BaseFrame1("A Popup Window");
19: window.resize(150,150);
20: window.show();
21: }
22:}

Listing 13.4: Die PopupActions-Klasse

 1: import java.awt.*;

2: import java.awt.event.*;
3:
4: public class PopupActions implements ActionListener {
5:
6: PopupWindow theApp;
7:
8: PopupActions(PopupWindow win) {
9: theApp = win;
10: }
11:
12: public void actionPerformed(ActionEvent e) {
13: if (e.getSource() instanceof Button) {
14:
15: if (e.getSource() == theApp.open) {
16: if (!theApp.window.isShowing())
17: theApp.window.show();
18: }
19: else if (e.getSource() == theApp.close) {
20: if (theApp.window.isShowing())
21: theApp.window.hide();
22: }
23: }
24: }
25:}

Listing 13.5: Die BaseFrame1-Klasse

 1: import java.awt.*;

2: import java.awt.event.*;
3:
4: class BaseFrame1 extends Frame {
5: String message = "This is a Window";
6: Label l;
7:
8: BaseFrame1(String title) {
9: super(title);
10: setLayout(new BorderLayout());
11:
12: l = new Label(message, Label.CENTER);
13: l.setFont(new Font("Helvetica", Font.PLAIN, 12));
14: add("Center", l);
15: }
16:
17: public Insets getInsets() {
18: return new Insets(20,0,25,0);
19: }
20:}

siehe Abbildung

Abbildung 13.9:
Fenster

Dieses Beispiel ist aus drei Klassen gebildet: Die erste, PopupWindow, ist die Applet-Klasse, die das Pop-up-Fenster erzeugt und steuert. In der init()-Methode dieser Klasse, und hier insbesondere in den Zeilen 10 bis 16 im Listing 13.3, fügen Sie zwei Schaltflächen in das Applet ein, die das Fenster steuern. Dann wird das Fenster selbst erstellt, in seiner Größe verändert und angezeigt.

Die Steuerung in diesem Applet wird aktiv, wenn eine der Schaltflächen aktiviert wird. An dieser Stelle kommt die zweite Klasse ins Spiel. PopupActions enthält den Code für die Ereignishandhabung dieser Schaltflächen, wobei ActionListener zur Reaktion auf die Schaltflächen implementiert ist. In dieser Klasse wird mit der Schaltfläche Open Window einfach nur das Fenster angezeigt, wenn es verborgen ist (Zeilen 15 bis 18 in Listing 13.4), und es wird verborgen, wenn es angezeigt ist (Zeilen 19 bis 21).

Das Pop-up-Fenster selbst ist ein spezieller Rahmen mit Namen BaseFrame1. Bie diesem Beispiel ist der Rahmen recht einfach: er nutzt ein BorderLayout und zeigt ein Label in der Mitte des Rahmens. Beachten Sie, daß die Initialisierung des Rahmens in einem Konstruktor erfolgt, und nicht in über eine init()-Methode. Da Rahmen normale Objekte und keine Applets sind, müssen Sie sie auf konventionelle Art und Weise initialisieren.

Im Konstruktor von BaseFrame1 sehen Sie, daß die erste Zeile (Zeile 9) einen Aufruf des Konstruktor der übergeordneten Klasse zu BaseFrame1 enthält. Wie Sie schon während Tag 6, »Erstellen von Klassen und Applikationen in Java«, gelernt haben, ist dieser Aufruf der erste Schritt bei der Initialisierung einer neuen Klasse. Vergessen Sie diesen Schritt nicht, wenn Sie Ihre eigenen Klassen erstellen – schließlich wissen Sie nie, welche wichtigen Dinge die Superklasse in dem Konstruktor macht.

Dialogfelder

Dialogfelder ähneln in ihrer Funktionsweise den Rahmen insoweit, daß ein neues Fenster am Bildschirm eingeblendet wird. Jedoch sind Dialogfelder als Übergangsfenster gedacht – z.B. Fenster, die Warnungen ausgeben, Sie nach bestimmten Informationen fragen etc. Dialoge haben für gewöhnlich keinen Titelleisten und zeigen auch viele andere allgemeine Fenstermerkmale nicht (Sie können jedoch ein Dialogfeld mit einer Titelleiste erstellen). Dialogfelder können als nicht in ihrer Größe veränderbar oder modal angelegt werden. (Modale Dialogfelder verhindern die Eingabe in einem anderen, derzeit angezeigten Fenster, bis das Dialogfeld wieder geschlossen ist).


Dialogfelder sind Übergangsfenster, die dazu dienen, den Benutzer über Ereignisse zu informieren oder Eingaben vom Benutzer anzufordern. Im Gegensatz zu Rahmen haben Dialogfelder im allgemeinen keine Titelleiste oder Schaltfläche zum Schließen des Feldes.

Ein modales Dialogfeld verhindert die Eingabe in einem anderen, derzeit angezeigten Fenster, bis das Dialogfeld geschlossen ist. Ein modales Dialogfenster können Sie nicht auf Symbolgröße reduzieren, und es können parallel keine anderen Fenster in den Vordegrund geholt werden. Das modale Dialogfeld muß erst geschlossen sein, bevor Sie im System weiterarbeiten können. Typische Beispiele für modale Dialogfelder sind Warnungen und Alarme.

Das AWT bietet zwei Arten von Dialogfeldern: die Dialog-Klasse, die ein allgemeines Dialogfeld enthält, und FileDialog, womit ein plattformspezifischer Datei-Browser erzeugt wird.

Dialogobjekte

Dialoge werden fast genauso erstellt und eingesetzt wie Fenster. Um einen allgemeinen Dialog zu erstellen, benutzen Sie einen der folgenden Konstruktoren:

Das Dialogfenster ist, wie auch das Rahmenfenster, ein Panel, in dem Sie Komponenten der Benutzeroberfläche anordnen und zeichnen sowie Grafikoperationen ausführen können. Wie auch andere Fenster, ist das Dialogfeld zunächst unsichtbar. Sie können jedoch mit show() und hide() angezeigt bzw. wieder verborgen werden.

Fügen wir jetzt in das Beispiel mit dem Pop-up-Fenster ein Dialogfeld ein. Von den drei Klassen in diesem Applet muß nur BaseFrame1 geändert werden. Hier ändern Sie die Klasse dahingehend, daß sie eine Schaltfläche Set Text und eine neue Klasse, TextDialog, enthält, die eine Texteingabedialogfeld ähnlich dem in Abbildung 13.10 gezeigten enthält.

siehe Abbildung

Abbildung 13.10:
Das Dialogfeld Enter Text


Im Codebeispiel auf der CD ist diese Version des Applet von der vorherigen Version getrennt. Ich habe eine neue Klasse namens BaseFrame2 für diesen Teil des Beispiels erstellt, sowie eine neue Klasse PopupWindowDialog.java, die das Applet darstellt, zu dem dieses Fenster gehört. Außerdem habe ich PopupActions2.java als die Klasse erstellt, die die Aktionen handhabt. Mit PopupWindowDialog.html kann diese Version des Applet angezeigt werden.

Um das Dialogfeld in die BaseFrame1-Klasse einzufügen, sind nur geringfügige Änderungen erforderlich. Zunächst brauchen Sie eine Instanzvariable, die den Dialog enthält, weil Sie sich in der gesamten Klasse darauf beziehen werden:

TextDialog dl;

In der Konstruktor-Methode von BaseFrame2 können Sie das Dialogfeld erzeugen (als Instanz der neuen Klasse TextDialog, die Sie in ein paar Minuten anlegen werden), es der Instanzvariablen dl zuweisen und in seiner Größe ändern (wie in den folgenden beiden Codezeilen gezeigt). Das Dialogfeld soll noch nicht angezeigt werden, weil es erst eingeblendet werden soll, wenn die Schaltfläche Set-Text-Schaltflächen aktiviert wird.

dl = new TextDialog(this, "Enter Text", true);

dl.resize(150,100);

Als nächstes erstellen Sie die Schaltfläche Set Text in ähnlicher Form wie andere Schaltflächen, und dann fügen Sie sie in BorderLayout an der Position "South" ein (d.h. direkt unterhalb des Label).

Button b = new Button("Set Text");

add("South", b);

Damit das Dialogfeld zur richtigen Zeit angezeigt wird, müssen Sie in diesen Rahmen die Ereignishandhabung aufnehmen. Ich habe das Ereignisverhalten in einer ActionListener-Klasse namens BaseFrameActions angegeben (siehe Listing 13.6). Dieser Code müßte Ihnen bekannt vorkommen. Deswegen werde ich ihn nicht nochmal im Detail erklären. Herauszustellen ist nur, daß eine actionPerformed()-Methode dl.show() im Basisrahmen aufruft, um das Dialogfeld anzuzeigen, sobald die Schaltfläche aktiviert wird.

Listing 13.6: BaseFrameActions

 1: import java.awt.*;

2: import java.awt.event.*;
3:
4: public class BaseFrameActions implements ActionListener {
5:
6: BaseFrame2 theApp;
7:
8: BaseFrameActions(BaseFrame2 win) {
9: theApp = win;
10: }
11:
12: public void actionPerformed(ActionEvent e) {
13: if (e.getSource() instanceof Button)
14: theApp.dl.show();
15: }
16:
17:}

Das ist das ganze Verhalten, das Sie im Pop-up-Fenster zur Erstellung eines Dialogfeldes einfügen müssen. Der Rest erfolgt in der TextDialog-Klasse. Der dazu erforderliche Code ist in Listing 13.7 angegeben

Listing 13.7: Die TextDialog-Klasse

 1: import java.awt.*;

2: import java.awt.event.*;
3:
4: class TextDialog extends Dialog implements ActionListener {
5: TextField tf;
6: BaseFrame2 theFrame;
7:
8: TextDialog(Frame parent, String title, boolean modal) {
9: super(parent, title, modal);
10:
11: theFrame = (BaseFrame2)parent;
12: setLayout(new BorderLayout(10,10));
13: setBackground(Color.white);
14: tf = new TextField(theFrame.message,20);
15: add("Center", tf);
16:
17: Button b = new Button("OK");
18: b.addActionListener(this);
19: add("South", b);
20: }
21:
22: public Insets insets() {
23: return new Insets(30,10,10,10);
24: }
25:
26: public void actionPerformed(ActionEvent e) {
27: if (e.getSource() instanceof Button) {
28: String label = ((Button)e.getSource()).getLabel();
29: if (label == "OK") {
30: hide();
31: theFrame.l.setText(tf.getText());
32: }
33: }
34: }
35:}

Bei diesem Code sind ein paar Punkte zu beachten. Zunächst einmal achten Sie darauf, daß im Gegensatz zu den anderen beiden Fenstern in diesem Applet die Ereignishandhabung innerhalb der Klasse stattfindet, so daß das Dialogfeld als sein eigener Listener dient. Manchmal ist es sinnvoll, den Code Ereignishandhabung separat zu schreiben, doch manchmal ist es einfacher, alles zusammen-zulassen. In diesem Fall ist das Element TextDialog einfach genug, so daß es einfacher ist, alles zusammenzustellen.

Trotzdem sind in diesem Dialogfeld viele Elemente mit denen der BaseFrame1-Klasse identisch. Beachten Sie, daß der Konstruktor für TextDialog mit dem Konstruktor der Superklasse Dialog identisch ist, den trotz der Tatsache, daß TextDialog einem Objekt verbunden ist, dessen Klasse BaseFrame2 ist, müssen die Dialogfelder einem Frame-Objekt zugeordnet werden. Es ist leichter, den Konstruktor allgemeiner zu erstellen und ihn anschließend zu spezialisieren, nachdem der Konstruktor der Superklasse aufgerufen wurde – und genau das geschieht in den Zeilen 9 und 11 in Listing 13.7. Zeile 9 enthält die Verzweigung zum Konstruktor der Superklasse, um den Dialog mit dem Rahmen zu verbinden. In Zeile 11 werden die Instanzvariablen auf die jeweilige Instanz der Frame-Klasse gesetzt, die in der Klasse BaseFrame2 definiert wurden.

Der Rest des TextDialog-Konstruktors richtet einfach nur das übrige Layout ein: ein Textfeld und eine Schaltfläche in einem BorderLayout. Mit der getInsets()-Methode werden ein paar Eckeinsätze hinzugefügt, und schließlich behandelt die actionPerformed()-Methode die Aktion der OK-Schaltfläche dieses Dialogfeldes. In der actionPerformed()-Methode passieren zwei Dinge: in Zeile 30 wird das Dialogfeld verborgen und freigegeben, und in Zeile 31 wird der Wert des Label im Eltern-Rahmen auf den neue Textwert geändert.

So viele Klassen für ein einfaches Applet! Die verschiedenen Fenster und die zugehörigen Ereignisklassen machen das Applet so kompliziert. An dieser Stelle sollten Sie aber bereits damit vertraut sein, daß jedes Teil eines Applet seine eigenen Komponenten und Aktionen hat, und wie alle diese Teile zusammenspielen. Sollte jedoch noch immer Verwirrung herrschen, können Sie den Beispielcode auf der CD durcharbeiten – PopupWindowDialog – um ein besseres Gefühl dafür zu bekommen, wie alles zusammengehört.

Applets und Dialogfelder zusammenfügen

Dialogfelder können nur einem Rahmen zugewiesen werden. Um ein Dialogfeld zu erstellen, müssen Sie eine Instanz der Frame-Klasse an eine der Konstruktor-Methoden des Dialogfeldes übergeben. Das würde bedeuten, daß Sie keine Dialogfelder erstellen können, die mit Applets verbunden sind. Da Applets keine expliziten Rahmen haben, können Sie der Dialog-Klasse kein Rahmen-Argument übergeben. Aber mit ein wenig trickreichem Code können Sie eines Frame-Objekts habhaft werden, das das Applet enthält (häufig im Browser- oder Apple-Viewer-Fenster) und das Objekt dann als Rahmen für das Dialogfeld nutzen.

Bei diesem Code wird die getParent()-Methode eingesetzt, die für alle AWT-Komponenten definiert ist. Die getParent()-Methode gibt das Objekt zurück, in dem dieses Objekt enthalten ist. Das Eltern-Objekt aller AWT-Applikationen muß also ein Rahmen sein. Applets verhalten sich genauso. Durch wiederholten Aufruf von getParent() müßten Sie schließlich in der Lage sein, eine Frame-Instanz zu erhalten. Hier nun der trickreiche Code, den Sie in Ihr Applet einfügen können:

Object anchorpoint = getParent()

while (! (anchorpoint instanceof Frame))
anchorpoint = ((Component)anchorpoint).getParent();

In der ersten Zeile erstellen Sie eine lokale Variable namens anchorpoint, die den schließlich gefundenen rahmen für das Applet aufnimmt. Das Objekt, das anchorpoint zugewiesen wird, kann eine von vielen Klassen sein. Sie deklarieren den Typ also als Object.

Die zweite Zeile in diesem Code ist eine while-Schleife, die für jedes neue Objekt in der Aufwärtskette getParent() aufruft, bis sie ein Frame-Objekt erreicht. Da die getParent()-Methode nur für Objekte definiert ist, die von Component erben, müssen Sie hier daran denken, daß Sie den Wert von anchorpoint jedes Mal in Component stellen müssen, damit die getParent()-Methode funktionieren kann.

Nach dem Austritt aus der Schleife wird das Objekt in der anchorpoint-Variable eine Instanz der Frame-Klasse (oder einer ihrer Subklasen). Anschließend können Sie ein Dialog-Objekt erstellen, das diesem Rahmen beigefügt wird, in dem Sie anchorpoint noch einmal ausgeben, um sicherzustellen, daß Sie wirklich Frame-Objekt haben:

TextDialog dl = new TextDialog((Frame)anchorpoint,

"Enter Text", true);

Dateidialog-Objekte

Die FileDialog-Klasse bietet ein standard Dialogfeld Datei öffnen/speichern, mit dem Sie auf das lokale Dateisystem zugreifen können. Diese FileDialog-Klasse ist systemunabhängig, jedoch wird je nach Plattform wir der normale Dialog für das Öffnen oder Speichern von Dateien angezeigt.


Bei Applets ist es abhängig vom Browser ob Sie Instanzen von FileDialog einsetzen können. Die meisten Browser erzeugen lediglich einen Fehler, wenn Sie es versuchen. FileDialog ist bei Stand-alone-Anwendungen wesentlich nützlicher.

Um einen Dateidialog zu erstellen, benutzen Sie die folgenden Konstruktoren:

Nachdem Sie eine FileDialog-Instanz erstellt haben, zeigen Sie sie mit show() an:

FileDialog fd = new FileDialog(this, "FileDialog");

fd.show();

Wenn der Benutzer eine Datei im Datei-Dialogfeld auswählt und es dann schließt, können Sie auf den vom Benutzer gewählten Dateinamen zugreifen, indem Sie die Methoden getDirectory() und getFile() verwenden. Beide Methoden geben Zeichenketten zurück, die die Werte angeben, die der Leser gewählt hat. Sie können die Datei dann mit den Stream- und File-Handler-Methoden (über die Sie nächste Woche mehr erfahren werden) öffnen und sie auslesen bzw. in sie schreiben.

Cursor (nur 1.02)

Sie haben bereits gelernt, wie Sie Cursor in der Version 1.1 im JDK einsetzen, und wo Sie den Cursor für die einzelnen im Applet oder in der Applikation verwendeten Komponenten definieren. Bei der Version 1.02 von JDK können Cursor nur in Frame-Objekten festgelegt werden und nur global für diesen Rahmen geändert werden – zum Beispiel um einen Wartezustand oder ein anderes Ereignis in Ihrem Programm darzustellen.

Die Methoden getCursorType() und setCursor() werden in der Frame-Klasse definiert. Wenn Sie ein Frame-Objekt haben, können Sie den Cursor festlegen. (Normalerweise bestimmen Sie Cursor für Fenster, aber Sie können auch Cursor für Applets über die getParent()-Methode festlegen, die im Abschnitt »Applets und Dialogfelder zusammenfügen« erläutert wurde). Beide Methoden nutzen dieselben vordefinierten Cursor-Typen, die in Tabelle 13.6 für 1.1 aufgeführt sind, mit der Ausnahme, daß diese Cursor durch die Frame-Klasse statt durch die Cursor-Klasse definiert werden (in 1.02 existiert die Cursor-Klasse nicht).

Um auf einen bestimmten Cursor zuzugreifen, verwenden Sie FRAME.Cursorname. Zum Beispiel: Frame.WAIT_CURSOR ist der standardmäßige Cursor bei Wartezuständen.

Fensterereignisse

Dies sind nun die letzten Ereignisse, die Sie in AWT berarbeiten können: die Ereignisse für Fenster und Dialogfelder. (In bezug auf Ereignisse sind Dialogfeld und Fenster gleich.) Fensterereignisse entstehen, wenn sich der Zustand eines Fensters in irgendeiner Form ändert: wenn das Fenster verschoben, seine Größe geändert, auf Symbolgröße reduziert, in den Vordergrund geholt oder geschlossen wird. In einer wohlerzogenen Applikation werden Sie zumindest einige dieser Ereignisse behandeln wollen – z.B. um Threads zu stoppen, wenn ein Fenster auf Symbolgröße reduziert wird, oder für Aufräumarbeiten, wenn das Fenster geschlossen wird.

Im 1.02-Ereignismodell verwenden Sie handleEvent() für den Test der einzelnen Ereignisse, die in Tabelle 13.8 genannt sind, indem Sie die normale switch-Anweisung mit der Instanzvariablen id benutzen.

Tabelle 13.8: Fensterereingisse in 1.02

Ereignisname

Wann es auftritt

WINDOW_DESTROY

Wird erzeugt, wenn ein Fenster über das Feld Schließen oder den Menüpunkt Schließen zerstört wird

WINDOW_EXPOSE

Wird erzeugt, wenn ein Fenster hinter einem anderen Fenster hervorgeholt wird

WINDOW_ICONIFY

Wird erzeugt, wenn das Fnster auf Symbolgröße reduziert wird

WINDOW_DEICONIFY

Wird erzeugt, wenn das Fenster aus Symbolgröße wiederhergestellt wird

WINDOW_MOVED

Wird erzeugt, wenn das Fenster verschoben wird

Im 1.1-Ereignismodell erzeugen Fensterereignisse leicht abweichende Aktionen, und Sie können mehr Ereignisse nutzen (z.B. gibt es eigene Ereignisse für das Schließen eines Fensters und wenn das Fenster komplett geschlossen ist). Die Fensterereignisse in 1.1 sind:

Das Window moved-Ereignis ist eine Ausnahme. Es wird wie ein weiteres Komponentenereignis behandelt (durch die ComponentEvent-Klasse repräsentiert), und der ComponentListener behandelt dieses. In Tabelle 13.9 stehen die Ereignisse, Listeners und Methoden für die einzelnen Fensterereignisse in 1.1.

Tabelle 13.9: Fensterereignisse in 1.1

Ereignisname

Listener

Methode

Window opened

WindowListener

public void windowOpened(WindowEvent e)

Window moved

ComponentListener

public void componentMoved(Component
Event e)

Window activated

WindowListener

public void windowActivated(WindowEvent e)

Window deactivated

WindowListener

public void windowDeactivated(Window-Event e)

Window iconified

WindowListener

public void windowIconified(WindowEvent e)

Window deiconified

WindowListener

public void windowDeiconified(Window-Event e)

Window closing

WindowListener

public void windowClosing(WindowEvent e)

Window closed

WindowListener

public void windowClosed(WindowEvent e)

Um Fenster-Listeners einzutragen, verwenden Sie die addWindowListener()-Methode. Beachten Sie, daß diese Methode nur bei Subklassen von Frame und Dialog zur Verfügung steht. Vergewissern Sie sich also, daß Sie Ihre Listeners in das oberste Fenster Ihres Applet bzw. Ihrer Applikation einfügen.

Menüs

Es bleibt jetzt nur noch ein Element der Benutzeroberfläche im AWT zu behandeln: Menüs. In Java 1.1 können Sie zwei verschiedene Arten von Menüs erstellen: feststehende Menüs, die in einer Menüleiste an der Oberkante des Fensters stehen, und Pop-up-Menüs, die überall in einer Applikation oder einem Applet erscheinen können. In Java 1.02 können Sie nur Menüs in der Menüleiste erzeugen.

Eine Menüleiste ist eine Zusammenstellung von Menüs. Ein Menü enthält dazu eine Zusammenstellung von Menüpunkten, die Namen haben und manchmal optional über Tastenkürzel verfügen. Ein Pop-up-Menü ist einfach nur ein Menü, das außerhalb einer Menüleiste existieren kann. Auch ein Pop-up-Menü enthält Menüpunkte, und diese Menüpunkte verhalten sich in derselben Weise. ATW bietet Klassen für alle diese Menüelement, darunter MenuBar, Menu, MenuItem und in 1.1 PopupMenu. Abbildung 13.11 zeigt die Menüklassen.

siehe Abbildung

Abbildung 13.11:
Die AWT-Menüklassen

Menüs und Menüleisten

Eine Menüleiste ist ein Satz von Menüs, die an der Oberkante eines Fensters erscheinen. Da sich Menüleisten immer auf Fenster beziehen, können Sie keine Menüleisten in Applets erstellen (aber wenn das Applet ein unabhängiges Fenster öffnet, kann dieses Fenster eine Menüleiste besitzen).

Um eine Menüleiste für ein bestimmtes Fenster zu erzeugen, legen Sie eine neue Instanz der Klasse MenuBar an:

MenuBar mbar = new MenuBar();

Um diese Menüleiste als Standardmenü des Fensters festzulegen, verwenden Sie die setMenuBar()-Methode (definiert in der Frame-Klasse):

window.setMenuBar(mbar);

Sie können individuelle Menüs (Datei, Bearbeiten etc.) in die Menüleiste einfügen, indem Sie sie erzeugen und dann in die Menüleiste mit der Methode add() einfügen. Das Argument für den Menu-Konstruktor ist der Name des Menüs, wie er in der Menüleiste auftritt.

Menu myMenu = new Menu("File");

mbar.add(myMenu);

Bei einigen Systemen kann ein spezielles Hilfemenü definert werden, das auf der rechten Seite der Menüleiste steht. Sie können ein bestimmtes Menü mit der setHelpMenu()-Methode als Hilfemenü definieren. Das angegebene Menü muß bereits dem Menü selbst hinzugefügt worden sein, bevor Sie es zu einem Hilfemenü machen können.

Menu helpmenu = new Menu("Help");

mbar.add(helpmenu);
mbar.setHelpMenu(helpmenu);

Falls Sie den Benutzer aus irgendeinem Grund daran hindern möchten, ein bestimmtes Menü auszuwählen, können Sie den Befehl disable() auf das Menü anwenden (bzw. den enable()-Befehl, um es wieder verfügbar zu machen):

myMenu.disable();

Menüoptionen

Sie können vier verschiedene Optionsarten in die Menüs einfügen:

In Java 1.1 können sowohl Menüoptionen sowie umschaltbare Menüoptionen über optionale Tastenkürzel verfügen, die anstelle von Mausoperationen zum Öffnen des Menüs und der Auswahl eines Menüpunktes eingesetzt werden können. In Java 1.02 ist diese Funktion nicht verfügbar.

Menüoptionen erstellen

Normale Menüoptionen werden in ein Menü mit der MenuItem-Klasse eingefügt. Zunächst erstellen Sie eine neue Instanz von MenuItem und fügen diese anschließend mit der add()-Methode in die Menu Komponente ein:

Menu myMenu = new Menu("Tools");

myMenu.add(new MenuItem("Info"));
myMenu.add(new MenuItem("Colors"));

Untermenüs können einfach durch Erstellen einer neuen Instanz von Menu und deren Hinzufügen in das erste Menü erstellt werden. Danach können Sie Optionen in das Menü einfügen:

Menu submenu = new Menu("Sizes");

myMenu.add(submenu);
submenu.add(new MenuItem("Small"));
submenu.add(new MenuItem("Medium"));
submenu.add(new MenuItem("Large"));

Die CheckBoxMenuItem-Klasse erzeugt eine Menüoption mit einem Kontrollfeld, wodurch der Zustand des Menüs ein- bzw. augeschaltet werden kann. (Einmaliges Anklicken bewirkt die Auswahl des Kontrollfelds, nochmaliges Anklicken bewirkt die Abwahl des Kontrollfelds.) Menoptionen mit Kontrollfeld werden auf die gleiche Art erzeugt, wie normale Menüoptionen:

CheckboxMenuItem coords =

new CheckboxMenuItem("Show Coordinates");
myMenu.add(coords);

Um schließlich einen Separator in das Menü einfügen (eine Linie, mit der Sie Optionsgruppen in einem Menü trennen), erstellen Sie eine Menüoption mit einem einfachen Bindestrich (–) als Label und fügen diese dann ein. Die spezielle Menüoption wird als Trennlinie ausgegeben. Die beiden nächsten Zeilen Java-Code erzeugen eine Seaparator-Menüoption und fügen Sie in das Menü myMenu ein:

MenuItem msep = new MenuItem("–");

myMenu.add(msep);

Jede Menüoption kann mit der disable()-Methode deaktiviert und mit enable() wieder aktiviert werden. Deaktivierte Menüoptionen können nicht ausgewählt werden.

MenuItem item = new MenuItem("Fill");

myMenu.addItem(item);
item.disable();

Tastenkürzel für Menüoptionen (nur in Java 1.1)

In Java 1.1 können sowohl Menüoptionen als auch Menüoptionen mit Kontrollfeld über Tastenkürzel verfügen, die Ihnen die Auswahl dieses Menüs über die Tastatur anstelle von Mausoperationen erlaubt. Wie bei der Tabulator-Taste beim Wechsel des Eingabefokus zwischen AWT-Komponenten, ist es dadurch beim 1.1. AWT möglich, ohne Maus zu arbeiten.

Um einer Menüoption ein Tastenkürzel hinzuzufügen, erstellen Sie eine Instanz der MenuShortcut-Klasse und übergeben dieses Tastenkürzel an den Konstruktor von MenuItem oder CheckboxMenuItem. MenuShortcut-Objekte können auf eine der beiden folgenden Arten erstellt werden:

Hier ein Beispiel mit drei Menüoptionen, von denen jede ihr eigene Tastenkürzel aufweist:

Menu editMenu = new Menu("Edit");

editMenu.add(new MenuItem("Cut", new MenuShortcut('x')));
editMenu.add(new MenuItem("Copy", new MenuShortcut('c')));
editMenu.add(new MenuItem("Paste", new MenuShortcut('v')));

Tastenkürzel für Menüs werden mit einer Modifizierungstaste aktiviert. Diese ist von Plattform zu Plattform verschieden. Bei Windows und Solaris ist dies (Strg), bei Macintosh ist es Command (Apfel). Durch Gedrückthalten der Modifizierungstaste und Drücken der Shortcut-Taste wird die Menüoption aktiviert. (Bei manchen Tastenkürzeln ist außerdem das Gedrückthalten der (ª)-Taste erforderlich.)

Nachdem ein Tastenkürzel für ein Menü festgelegt wurde, können zur Änderung dieses Kürzels mehrere Methoden eingesetzt werden. Bei der MenuItem-Klasse können getShortcut(), setShortcut() und deleteShortcut() zum Abrufen, Ändern oder Löschen des Kürzels eingesetzt werden. Außerdem verfügt die MenuBar-Klasse über einer Reihe von Methoden: getShortcutMenuItem gibt die Menüoption zurück, die ein bestimmtes Kürzel enthält, shortcuts() gibt ein Enumeration-Objekt mit allen Kürzeln der Menüs in der Menüleiste zurück und deleteShortcut löscht ein bestimmtes Tastenkürzel.

Menüereignisse

Die Auswahl einer Menüoption oder die Auswahl eines Tastenkürzels für eine Menüoption führt sowohl beim in 1.02- als auch beim 1.1-Ereignismodell dazu, daß ein Aktionsereignis generiert wird. Sie können das Ereignis mit der action()-Methode in 1.02 oder durch Implementieren eines Aktion-Listener behandeln, wie Sie es in den letzten Tagen gemacht haben. Beim 1.1-Ereignismodell müssen Aktionsereignisse für die einzelnen Menüoptionen registriert werden.

Zusätzlich zu den Aktionsereignissen erzeugen CheckBoxMenuItems Listenauswahl- und Listenabwahlereignisse, die im 1.02-Ereignismodell über handleEvent(), oder durch Implementieren der itemStateChanged()-Methode des ItemListener im 1.1-Ereignismodell behandelt werden.

Bei der Vearbeitung von Ereignissen, die von Menüoptionen und Menüoptionen mit Kontrollfeld erzeugt wurden, müssen Sie an folgendes denken: Da CheckboxMenuItem eine untergeordnete Klasse von MenuItem ist, brauchen Sie diese Menüoption nicht als Sonderfall zu behandeln. Diese Aktion handhaben Sie genauso wie andere Aktionsmethoden.

Pop-up-Menüs (nur bei 1.1)

Beim AWT 1.1 sind die Pop-up-Menüs neu hinzugekommen – eine neue Komponente, die Ihnen das Erstellen von Menüs erlaubt, die als Reaktion auf Mausklick-Ereignisse eingeblendet werden. Verschiedene Komponenten können unterschiedliche Pop-up-Menüs aufweisen. Dadurch können Sie kontextsensitive Menüs erstellen. Und da Pop-up-Menüs keine Menüleiste und kein Fenster benötigen, können Sie sie an einer beliebigen Stelle in Ihrer Applikation oder in Ihrem Applet positionieren.

Ein Pop-up-Menü ist lediglich eine Sonderform eines Menüs. Es kann Menüoptionen enthalten, und jeder Menüoption ein Aktionsereignis zugeordnet sein. Um ein neues Pop-up-Menü zu erstellen, verwenden Sie die PopupMenu-Klasse:

PopupMenu popmenu = new PopupMenu("Actions");

Die Menüoptionen des Pop-up-Menüs fügen Sie so wie bei normalen Menüs ein:

MenuItem micut = new MenuItem("Cut");

micut.addActionlistener(this);
popmenu.add(miout);
MenuItem micopy = new MenuItem("Copy");
micopy.addActionlistener(this);
popmenu.add(micopy);
MenuItem mipaste = new MenuItem("Paste");
mipaste.addActionlistener(this);
popmenu.add(mipaste);

Nachdem das Menü aufgebaut ist, fügen Sie es mit add() in die Komponente ein:

add(popmenu);

Mit der show()-Methode, die in der PopupMenu-Klasse definiert wurde, lassen Sie das Pop-up-Menü an einer besimmten Position anzeigen. Im allgemeinen rufen Sie show() als Reaktion auf ein Mausereignis auf.

Hier stellt sich das Problem, daß unterschiedliche Plattformen unterschiedliche Ereignisse als Auslöser für ein Pop-up-Menü nutzen. Windows blendet ein Fenster als Reaktion auf ein Loslassen der rechten Maustaste ein, wohingegen Solaris bei einem Drücken der rechten Maustaste reagiert. Bei Pop-up-Menüs verwenden Sie am besten die isPopupTrigger()-Methode innerhalb von MouseEvent und eine Komponentenmethode namens processMouseEvent(), die dazu dienen, Mausereignisse in einer allgemeinen Art und Weise zu handhaben (ähnlich der handleEvent()-Methode im 1.02-Ereignismodell). Dieser Code (innerhalb eines Objekts der Component-Klasse oder einer davon abgeleiteten Klasse) bringt das Menü zur richtigen Zeit auf den Bildschirm:

public void processMouseEvent(MouseEvent e) {

if (e.isPopupTrigger())
popup.show(e.getComponent(), e.getX(), e.getY());

super.processMouseEvent(e);
}


Die processMouseEvent()-Methode ist nur eine der allgemeinen Komponentenereignisse, die zur Realisierung des auf dem Listener basierenden Ereignismodell in 1.1 dienen. Meistens werden Sie keine dieser Methoden brauchen (die alle in der Component-Klasse definiert wurden). Der einzige Fall, den Sie möglicherweise einsetzen werden, ist dann, wenn Sie Ihre eigenen individuellen Komponenten der Benutzeroberfläche definieren – doch das würde die Grenzen dieses Buchs sprengen. In der API-Dokumentation zur Component-Klasse und der Ereignisspezifikation (siehe http://java-sun.com/products/jdk/1.1/docs/guide/awt/designspec/events.html) finden Sie weitere Details hierzu.

Beispiel: Ein Pop-up-Fenster mit Menüs

Zum Abschluß diese Tages fügen wir ein Menü in das Pop-up-Fenster-Applet ein, das wir im Abschnitt der Dialogfelder erstellt haben. Dazu sind ein paar Schritte erforderlich: Erstellen und Hinzufügen des Menüs mit allen seinen Menüoptionen in das Layout der BaseFrame-Klasse und anschließend Modifizieren der Aktionsmethode in BaseFrameActions, um die Aktionen der Menüoptionen zu bearbeiten.

Beginnen wir mit der BaseFrame3-Klasse und ändern das Layout. Listing 13.8 zeigt den neuen Code, und Abbildung 13.12 zeigt das Menü Menü in Aktion.


Im Beispielcode auf der CD, sind die Klassen, die Sie für dieses Applet nachschlagen: PopupWindowMenu.java, PopupActions2.java, BaseFrame3.java, BaseFrameActions2.java und TextDialog2.java. Sie können PopupWindowMenu.html zur Anzeige benutzen.

Listing 13.8: BaseFrame3 mit einem Menü

 1: import java.awt.*;

2:
3: class BaseFrame3 extends Frame {
4: String message = "This is a Window";
5: TextDialog2 dl;
6: Label l;
7:
8: BaseFrame3(String title) {
9: super(title);
10: setLayout(new BorderLayout());
11:
12: l = new Label(message, Label.CENTER);
13: l.setFont(new Font("Helvetica", Font.PLAIN, 12));
14: add("Center", l);
15:
16: // Dialog für dieses Fenster erstellen
17: dl = new TextDialog(this, "Enter Text", true);
18: dl.resize(150,100);
19:
20: Button b = new Button("Set Text");
21: BaseFrameActions handleact = new BaseFrameActions(this);;
22: b.addActionListener(handleact);
23: add("South", b);
24:
25: MenuBar mb = new MenuBar();
26: Menu m = new Menu("Colors");
27: MenuItem redmi = new MenuItem("Red");
28: redmi.addActionListener(handleact);
29: m.add(redmi);
30: MenuItem bluemi = new MenuItem("Blue");
31: bluemi.addActionListener(handleact);
32: m.add(bluemi);
33: MenuItem greenmi = new MenuItem("Green");
34: greenmi.addActionListener(handleact);
35: m.add(greenmi);
36: m.add(new MenuItem("–"));
37: CheckboxMenuItem boldmi = new CheckboxMenuItem("Bold Text");
38: boldmi.addActionListener(handleact);
39: m.add(boldmi);
40: mb.add(m);
41: setMenuBar(mb);
42: }
43:
44: public Insets getInsets() {
45: return new Insets(20,0,25,0);
46: }
47:}

siehe Abbildung

Abbildung 13.12:
Ein Menü

Dieses Menü enthält vier Optionen: je eine für die Farben Rot, Blau und Grün (die den Hintergrund des Fensters ändern, wenn sie ausgewählt werden), und eine Menüoption mit einem Kontrollfeld, um den Text in Fettdruck darzustellen. Die Optionen werden alle als Teil des Konstruktor in dieser Klasse eingefügt, siehe Zeilen 25 bis 41. Neben dem Erstellen der Menüoptionen selbst, haben Sie auch die Aktion-Listeners jeder der Optionen hinzugefügt – eine Instanz der BaseFrame-Actions-Klasse mit den Aktion-Listeners.

Um die Menüoptionen selbst zu behandeln, modifizieren Sie die Klasse BaseFrameActions so, daß in der actionPerformed()-Methode Menüoptionen stehen. Vorher hatte diese Methode nur einen Eintrag für die Schaltfläche Set Text. Jetzt fügen Sie Methoden zur Änderung der Label-Hintergrundfarbe als Reaktion auf die Farben ein, oder zur Änderung der Beschriftung selbst in Fettdruck als Reaktion auf die Menüoption Bold Text. Hier der Code zur neuen actionPerformed()-Methode aus der BaseFrame3-Klasse:

public void actionPerformed(ActionEvent e) {

if (e.getSource() instanceof Button)
theApp.dl.show();
else if (e.getSource() instanceof MenuItem) {
String label = ((MenuItem)e.getSource()).getLabel();
if (label.equals("Red"))
theApp.l.setBackground(Color.red);
else if (label.equals("Blue"))
theApp.l.setBackground(Color.blue);
else if (label.equals("Green"))
theApp.l.setBackground(Color.green);
else if (label.equals("Bold Text")) {
if (theApp.l.getFont().isPlain()) {
theApp.l.setFont(new Font("Helvetica", Font.BOLD, 12));
}
else theApp.l.setFont(new Font("Helvetica", Font.PLAIN, 12));
}
}
}

Zur Behandlung dieser Menüoptionen, wenn Sie gewählt wurden, testen Sie, ob die Aktion von einer Menüoption stammt (was drei Menüoptionen mit Kontrollfeld enthält), und wenn ja, testen Sie wiederum jedes Menü-Label. Bei den Menüoptionen Rot, Blau und Grün brauchen Sie nur die Hintergrundfarbe des Label festzulegen. Bei der Umschaltoption Bold Text testen Sie erst, ob der Text derzeit normal dargestellt ist (mit der isPlain()-Methode), und wenn ja, ersetzen Sie ihn durch einen Fettruck-Font; ansonsten stellen Sie den Text wieder mit normalem Font dar.

AWT-Stand-alone-Applikationen erstellen

Nach all diesem Papier und der Zeit, die ich bis jetzt auf die Erstellung von Applets verwendet habe, werden Sie sich wundern, daß ich hier am Ende eine Beschreibung grafischer Java-Applikationen einfüge, und das in einem kleinen Abschnitt. Der Grund ist, daß Sie außer in ein paar einfachen Codezeilen und in den verschiedenen Umgebungen keine großen Unterschiede zwischen einem Java-Applet und einer grafischen Java-Applikation finden werden. Alles das, was Sie bis jetzt über das AWT gelernt haben, einschließlich der Grafikmethoden, Animationstechniken, Ereignisse, Komponenten der Benutzeroberfläche sowie Fenster und Dialogfelder, kann genauso in Java-Applikationen wie in Applets eingesetzt werden. Und Applikationen haben den Vorteil, »nicht mehr im Sandkasten zu stecken« – sie unterliegen nicht den Sicherheitsbeschränkungen wie Applets. Mit Applets können Sie (fast) alles tun, was Sie wollen.

Wie erstellen wir also eine grafische Java-Applikation? Der Code dafür ist nahezu trivial. Ihre Hauptanwendungsklasse muß von Frame abgeleitet sein. Werden Threads genutzt (für die Animation oder andere Verarbeitungsarten), muß auch Runnable implementiert sein

class MyAWTApplication extends Frame implements Runnable {

...
}

Innerhalb der main()-Methode Ihrer Applikation erstellen Sie eine neue Instanz Ihrer Klasse – weil Ihre Klasse Frame erweitert, und damit erhalten Sie ein neues AWT-Fenster, das Sie in seiner Größe ändern und wie ein beliebiges anderes AWT-Fenster anzeigen können.

In der Konstruktor-Methode Ihrer Klasse richten Sie die üblichen AWT-Funktionen für Fenster ein, wie Sie es normalerweise in der init()-Methode eines Applet täten: Legen Sie den Titel fest, fügen Sie den Layout-Manager ein, Erstellen Sie Komponenten wie eine Menüleiste oder andere Elemente der Benutzeroberfläche und fügen sie ein, starten Sie ein Thread etc.

Hier ein ganz einfaches Applikationsbeispiel:

import java.awt.*;


class MyAWTApplication extends Frame {

MyAWTApplication(String title) {
super(title);

setLayout(new FlowLayout());
add(new Button("OK"));
add(new Button("Reset"));
add(new Button("Cancel"));
}

public static void main(String args[]) {
MyAWTApplication app = new MyAWTApplication("Hi! I'm an application");
app.resize(300,300);
app.show();
}
}

Meistens können Sie die diese Woche erlernten Methoden einsetzen, um Ihre Applikation zu steuern und zu verwalten. Nur die für Applets spezifischen Methoden können Sie nicht verwenden (d.h. solche, die in java.applet.Applet definiert sind, darunter Methoden zum Erhalt von URL-Informationen und zum Abspielen von Audio-Clips). Weitere Einzelheiten hierzu finden Sie in der API-Dokumentation zu dieser Klasse.

Und noch einen Unterschied zwischen Applikationen und Applets sollten Sie kennen: Wenn Sie ein Fensterschließereignis behandeln, müssen Sie neben dem Verbergen oder Zerstören des Fensters auch System.exit(0) aufrufen, um das System zu informieren, daß Ihre Applikation beendet wurde:

public void windowClosing(WindowEvent e) {

win.hide();
win.destroy();
System.exit(0);
}

Zusammenfassung

Die heutige Lektion war lang – dafür haben Sie aber alles gelernt, was Sie für den pratischen Einsatz des AWT benötigen. Heute haben Sie mehr über die verschiedenen AWT-Komponenten erfahren, darunter Textbereiche, Listenfelder, Rollfelder, Bildlaufleisten, Zeichenbereiche und Cursor. Und Sie haben das Verschachteln von Panels und Komponenten sowie die verschiedenen Methoden und Ereignisse, die Sie bei Komponenten einsetzen können, kennengelernt. Der zweite Teil des Tages beschäftigte sich mit Fenstern, Dialogfeldern und Menüs, und wie das AWT bei Stand-alone-Applikationen zu nutzen ist. Während der Lektion haben Sie eine Reihe von Beispiel-Applets durchgearbeitet, so daß Sie ein Gefühl dafür bekommen konnten, wie die Teile eines Applet oder einer Applikation zusammenspielen.

Glückwunsch! Das Schlimmste haben Sie geschafft! Morgen geht es dann mit ein paar Themen weiter, die ich hier noch nicht behandelt habe, und dann können Sie in der dritten Woche mit den komplexeren Teile der Sprache Java fortfahren.

Fragen und Antworten

F Ich versuche, eine individuell angepaßte Komponente zu erstellen, die eigentlich ein Satz Schaltflächen in einer scrollbaren Liste ist. Ich benutze 1.02, und es scheint, daß die einzige Möglichkeit hierfür ist, daß ich eine Komponente erstelle, die aus einem Panel und einer Bildlaufleiste besteht, und selbst die verschiedenen Bildlaufleistenereignisse abzufangen und das Scrollen zu behandeln. Ich kann einfach nicht glauben, daß ich diesen ganzen Code schreiben muß, um das Scrollen zu bearbeiten. Gibt es nicht eine einfachere Möglichkeit?

A Ja. Rüsten Sie auf 1.1 auf. Die ScrollPane-Klasse macht genau das, was Sie brauchen. Mit ScrollPane können Sie einfach ein Panel in das Rollfeld einfügen und dann in dem Panel Schaltflächen plazieren. Das ganze Scrollen wird für Sie bearbeitet. Diese Lösung ist viel einfacher und wurde eigentlich nur aus dem von Ihnen genannten Grund in das 1.1-AWT aufgenommen.

F Bei dem ColorTest-Beispiel, das RGB nach HSB konvertiert, kann ich die Farbanzeige nicht dazu bewegen, die Farbe zu ändern. Ich benutze den Code von der CD, und es funktioniert nicht. Was mache ich falsch?

A Verwenden Sie den Internet Explorer als Browser? Das fragliche Applet läuft unter Netscape und im SUN Applet-Viewer, aber wie ich gehört habe, läuft es im Explorer nicht richtig. Ich habe keine Antwort für Sie, außer daß Sie sich mit Microsoft über diesen Programmfehler unterhalten.

F Ich hasse diese riesige gelbe Warnung, die in Applet-basierten Fenster erscheint. Sie ist häßlich, und was noch schlimmer ist, sie verdeckt einen Teil meines Fensters. Kann ich sie irgendwie loswerden?

A Nein. Die Warnung muß bleiben. Was das Verdecken des Fensters angeht: Die Warnung schneidet tatsächlich den unteren Teil des Fensters ab (und auch Menüleisten, wenn Sie sie benutzen, schneiden das Fenster oben ab). Am besten umgehen Sie dieses Problem, indem Sie Eckeinsätze von 25 Punkten einsetzen, damit alle Teile Ihres Fensters im sichtbaren Bereich des Fensters bleiben.

F Bei der Diskussion der Stand-alone-Applikationen habe ich den Eindruck gewonnen, daß absolut kein Unterschied zwischen einem Applet und einer Applikation besteht. Wieso?

A Sowohl Applets als auch Applikationen verwenden genau dieselben Prozeduren im AWT, um Komponenten aufzubauen und anzuzeigen sowie Ereignisse zu behandeln. Die Unterschiede liegen nur darin, daß Applikationen von main() initialisiert und in ihrem eigenen Fenster angezeigt werden, und daß Applets von init() und start() initialisiert bzw. gestartet werden. Bei der großen Ähnlichkeit zwischen Applets und Applikationen, können Sie 99% dessen, was Sie hinsichtlich Applets gelernt haben, bei Applikationen verwenden. Und weil Applets die main()-Methode ignorieren, sollte Sie in einer Klasse existieren, gibt es keinen Grund, daß Sie nicht ein einzelnes Programm erstellen, das genauso gut als Applet und als Applikation läuft.

F Ich habe eine Stand-alone-Applikation erstellt. Aber wenn ich auf das Schließen-Schaltfeld klicke, passiert nichts. Was muß ich machen, damit meine Anwendung wirklich schließt?

A Verfolgen Sie das Fensterschließereignis entweder mit WINDOW_CLOSE im 1.02-Ereignismodell oder mit der windowClosing()-Methode im WindowListener bei 1.1. Als Reaktion auf dieses Ereignis rufen Sie hide() auf, wenn das Fenster später wieder auftreten kann, oder rufen Sie destroy() auf, um es ein für alle Mal loszuwerden. Führt das Fensterschließereignis zum kompletten Beenden Ihrer Applikation, rufen Sie auch System.exit() auf.


(c) 1997 SAMS
Ein Imprint des Markt&Technik Buch- und Software- Verlag GmbH
Elektronische Fassung des Titels: Java in 21 Tagen, ISBN: 3-8272-2009-2

Previous Page Page Top TOC Index Next Page See Page