11
von
Laura Lemay
Java-Ereignisse sind ein Teil des Java Abstract Windowing Toolkit (AWT). Ein Ereignis ist jene Art, in der AWT Ihnen als Programmierer und anderen Java-AWT-Komponenten mitteilt, daß etwas geschehen ist. Bei diesem »Etwas« kann es sich um eine Benutzereingabe (Mausbewegungen oder Klicks bzw. Tastenanschläge), Änderungen in der Systemumgebung (das Schließen und Öffnen von Fenstern bzw. das Blättern innerhalb von Fenstern) oder das Öffnen und Schließen eines Host handeln, also all jene Aktivitäten, welche die Ausführung des Programms in irgendeiner Weise betreffen. Indem Sie solche Ereignisse in einem Java-Programm erfassen, kann das Programm auf Benutzereingaben reagieren und sein Verhalten auf der Basis dieser Eingaben ändern.
Am heutigen Tag sollen die Ereignismodelle 1.02 und 1.1 erläutert werden. In der ersten Hälfte dieser Lektion lernen Sie zunächst das ältere Modell kennen; in der zweiten Hälfte wird dann die neuere Version 1.1 vorgestellt. Hier erfahren Sie auch, wie Sie alte Codes für das neue Modell konvertieren können. Um es genauer auszudrücken, heute werden folgende Themen behandelt:
Morgen erfahren Sie, wie sich diese grundlegenden Kenntnisse zu den Ereignissen in Ihren Programmen verwenden lassen und wie die Ereignisbehandlung in den beiden Ereignismodellen 1.02 und 1.1 funktioniert. Unabhängig davon, welches Ereignismodell Sie verwenden, sollten Sie zunächst den allgemeinen Überblick im folgenden Abschnitt durchlesen.
Ein Ereignis wird, wie bereits erwähnt, in Antwort auf etwas erzeugt, das während des Lebenszyklus eines Applet oder einer Anwendung geschieht. Jede Mausbewegung, jedes Klicken mit einer Maustaste, jeder Tastenanschlag und alle Aktionen mit Elementen der Benutzeroberfläche bzw. Operationen des Fensters alle diese Momente erzeugen ein Ereignis. Das heißt, während der Ausführung eines Java-Programms, können verschiedene Ereignisse stattfinden. Im Grunde sind Ereignisse in Java Objekte; sie sind Instanzen einer besonderen Ereignisklasse.
In Ihrem Applet-Code müssen Sie sich nicht mit all jenen Ereignissen befassen, die potentiell möglich sind das wäre ein schlechter Spaß. Manche Ereignisse können Sie sogar überhaupt nicht behandeln statt dessen erledigt dies das Operationssystem oder die Java-Umgebung für Sie. Das Zeichnen ist zum Beispiel ein Ereignis, das Sie nicht behandeln können; Sie können AWT lediglich darüber informieren, was gezeichnet werden soll, wenn es zu diesem Teil des Fensters gelangt.
Bei einigen Ereignissen können Sie jedoch intervenieren und es mag für Ihre Programme von Interesse sein, das eine oder andere Ereignis zu behandeln. Wenn der Benutzer eine beliebigen Position innerhalb eines Applet mit der Maus anklickt oder eine Taste drückt, können Sie eine Antwort auf dieses Ereignis geben. Dies ist der Sinn der Ereignisbehandlung im AWT. Um sich die Funktionsweise von Ereignissen deutlich zu machen, stellen Sie sich vor, Sie stehen an einem Fließband, auf dem die verschiedensten Ereignis-Objekte vorbeiziehen. Sie warten auf das richtige Objekt und wenn es vorbeikommt, nehmen Sie es vom Fließband herunter, um etwas damit zu tun. Kommt ein Objekt auf dem Fließband vorbei, mit dem Sie nichts anstellen möchten, können Sie es getrost vorbeiziehen lassen; in diesem Fall bearbeitet ein Teil des Systems das Objekt oder es bleibt ohne Auswirkungen.
Im folgenden sind jene Ereignisse aufgeführt, die Sie in Ihren eigenen Programmen behandeln können:
Heute erfahren Sie, wie Sie mit Ereignissen der Maus und der Tastatur (die ersten drei Punkte) verfahren können. In den nächsten Tagen wird beschrieben, wie Sie Elemente der AWT-Benutzeroberfläche erstellen und verwenden können, bei dieser Gelegenheit erfahren Sie auch, wie Sie mit Ereignissen der Benutzeroberfläche und Fensterbedienung umgehen können.
Ereignisse werden erzeugt und fließen dann meist auf dieselbe Weise durch das System, unabhängig davon, ob Sie das Ereignismodell 1.02 oder 1.1 in Ihrem Code verwenden. Der Unterschied zwischen den Modellen besteht vor allem darin, wie Sie diese Ereignisse erhalten und verarbeiten.
Im Ereignismodell 1.02, fließen alle Ereignisse, die während des Lebenszyklus eine Java-Programms stattfinden, durch dieses Programm und werden von einer Methode namens handleEvent() behandelt. Die grundlegende Version von handleEvent(), definiert in java.awt.Component (von der die Applet-Klasse abgeleitet wird) ruft verschiedene Standard-Methoden für verschiedene Ereignisse auf: mouseDown(), mouseUp(), keyDown() usw. Um ein Ereignis in einem Applet zu behandeln, müssen Sie lediglich eine dieser spezielle Ereignismethoden überschreiben. Sobald dann dieses Ereignis eintritt, wird die Methode aufgerufen. Wenn Sie zum Beispiel die Methode mouseDown() in einem Applet überschreiben, um eine beliebige Meldung auszugeben, erscheint diese, sobald das betreffende Ereignis vom Benutzer in Ihrem Applet ausgelöst wird.
Im Ereignismodell 1.1 unterscheidet sich der Fluß der Ereignisse und die Art deren Behandlung in Ihrem Programm. Das Ereignismodell 1.1 verwendet das Konzept der Event-Listener (»Ereigniszuhörer«). Die verschiedenen Listener sind verantwortlich für die Behandlung einer bestimmten Art von Ereignissen, zum Beispiel würden Sie einen Mouse-Listener für Mausereignisse definieren und einen Key-Listener für Tastatur-Ereignisse.
Innerhalb des Applet registrieren Sie diese Listener als Interessenten für diese Ereignisse (dazu lassen sich verschiedene Methoden verwenden). Nachdem ein Listener registriert ist, werden nur jene Ereignisse durch das Applet geleitet, für die Listener vorgesehen sind. Das Listener-Objekt ist dann für die Behandlung der Ereignisse zuständig, sobald diese ausgelöst werden.
Welches Modell Sie in Ihren Anwendungen verwenden sollten, hängt vor allem davon ab, in welcher Umgebung Sie das Programm ausführen. Die große Mehrzahl der Browser und anderer virtueller Java-Maschinen sind nur mit dem Ereignismodell 1.02 kompatibel (und dem JDK 1.02). Wenn Sie Ihr Applet primär in diesen Umgebungen ausführen lassen möchten, werden Sie vermutlich weiter mit dem Ereignismodell 1.02 arbeiten (das Ereignismodell 1.1 funktioniert in der 1.02-Umgebung nicht).
Sie können das Ereignismodell 1.02 aber auch in der 1.1-Umgebung benutzen; 1.1 ist abwärts zu älteren Versionen kompatibel. Wenn Sie der Ansicht sind, das Ereignismodell 1.02 sei einfacher zu verstehen und zu implementieren (dies ist vermutlich der Fall), können Sie dies auch noch weiterhin eine Weile verwenden.
Wenn Sie ausschließlich in der Java-1.1-Umgebung arbeiten und längere Anwendungen implementieren oder Anwendungen die JavaBeans verwenden, sollten Sie das Ereignismodell 1.1 benutzen.
Auch wenn Sie weiterhin mit dem Ereignismodell 1.02 arbeiten, ist es sicher ratsam, sich bereits einmal mit dem Konzept des Ereignismodells 1.1 vertraut zu machen, denn dieses verbreitet sich in Zukunft weiter und es kann sein, daß das Ereignismodell 1.02 von Java dann nicht mehr unterstützt wird. Das Modell 1.1 ist auf lange Sicht flexibler und läßt sich für umfangreiche Anwendungen besser einsetzen.
Mit all diesen allgemeinen Abwägungen im Hintergrund lassen Sie uns nun zum Kern der Sache kommen und die Codes für die Ereignisbehandlung betrachten. In den nächsten drei Abschnitten arbeiten Sie mit dem Ereignismodell 1.02, um Mausklicks, Mausbewegungen und Tastenanschläge zu behandeln. Wenn Sie sich ausschließlich für das Ereignismodell 1.1 interessieren, können Sie diese Abschnitt kurz für Anregungen durchlesen oder direkt mit dem Abschnitt zum Ereignismodell 1.1 fortfahren.
Zu den gängigsten Ereignisse in einem Applet zählen die Mausklicks. Mausklickereignisse finden statt, wenn ein Benutzer eine beliebige Position innerhalb des Applet anklickt. Sie können Mausklicks für einfache Dinge einsetzen, z.B. um Klänge im Applet an- oder auszuschalten, um zum nächsten Dia in einer Präsentation zu gelangen oder den Bildschirm zu leeren und neu zu beginnen. Mausklicks lassen sich auch in Verbindung mit Mausbewegungen benutzen, wenn in einem Applet komplexere Bewegungen durchgeführt werden sollen.
Wenn Sie einmal mit der Maus klicken, erzeugt AWT 1.02 zwei separate Ereignisse: Ein mouseDown-Ereignis, wenn die Maustaste gedrückt wird und ein mouseUp-Ereignis, wenn sie wieder losgelassen wird. Warum zwei verschiedene Ereignisse für eine einzige Mausaktion? Weil Sie vielleicht für »Down« und »Up« verschiedene Dinge festlegen möchten. Als Beispiel soll ein Pull-down-Menü dienen: Wenn Sie die Maustaste drücken, wird das Menü angezeigt, sobald Sie die Maustaste über einer Option loslassen, wird diese ausgewählt dazwischen wird die Maus gezogen (doch dazu später mehr). Wenn Sie nur ein Ereignis für beide Aktionen (mouseDown und mouseUp) zur Verfügung hätten, ließe sich diese Art von Benutzerinterkation nicht implementieren.
Die Behandlung von Mausereignissen in einem Applet ist einfach. Sie überschreiben lediglich die entsprechende Methodendefinition. Diese Methode wird aufgerufen, wenn das betreffende Ereignis stattfindet. Im folgenden finden Sie ein Beispiel der Methodenunterschrift für ein mouseDown-Ereignis:
public boolean mouseDown(Event evt, int x, int y) {
...
}
Die mouseDown-Methode (und auch die mouseUp-Methode) erhalten drei Parameter: Das Ereignis und die X- und Y-Koordinaten, an denen das mouseDown- oder mouseUp-Ereignis stattfindet.
Das Ereignisargument evt ist eine Instanz der Klasse Event. Alle Systemereignisse erzeugen eine Instanz der Event-Klasse, die Informationen darüber enthält, wo und wann ein Ereignis stattfindet, um welches Ereignis es sich handelt und andere Informationen, die über ein Ereignis von Interesse sind. Gelegentlich ist es sinnvoll, für ein Ereignisobjekt eine Referenz zu erstellen, wie Sie später in dieser Lektion noch erfahren werden.
Die X- und Y-Koordinaten des Ereignisses werden von den X- und Y-Argumenten weitergegeben und sind besonders nützlich, weil Sie auf ihrer Grundlage genau festlegen können, wo der Mausklick erfolgt ist. Wenn z.B. das mouseDown-Ereignis über einer grafischen Schaltfläche stattgefunden hat, könnten Sie diese Schaltfläche aktivieren. Beachten Sie, daß Sie die X- und Y-Koordinaten innerhalb des Ereignisobjekts selbst erhalten können; auf diese Weise werden sie als separate Variablen weitergeleitet, wodurch sie einfacher zu behandeln sind.
Im folgenden finden Sie eine einfache Methode, die Informationen über ein mouseDown-Ereignis zum Zeitpunkt der Ausführung ausgibt:
public boolean mouseDown(Event evt, int x, int y) {
System.out.println("Mouse down at " + x + "," + y);
return true;
}
Indem Sie die Methode in das Applet einfügen, wird diese Meldung jedesmal ausgegeben, wenn der Benutzer mit der Maus auf das Applet klickt. Das AWT-System ruft jede einzelne Methode auf, wenn das betreffende Ereignis stattfindet.
Beachten Sie, daß diese Methode, anders als die bisher erläuterten Systemmethoden, anstelle von void einen booleschen Wert ausgibt. Die Bedeutung dieses Unterschieds wird morgen klar, wenn Sie Benutzeroberflächen erstellen und Eingaben für diese Oberflächen definieren. Mit einer Ereignisbehandlung, die true oder false ausgibt, bestimmen Sie, ob eine bestimmte Komponente der Benutzeroberfläche in ein Ereignis eingreifen kann oder ob es an eine benachbarte Komponente abzugeben ist. Die allgemeine Regel lautet, daß Methoden, die mit Ereignissen zu tun haben, true zurückgeben sollten. Wenn die Methode aus irgendeinem Grund nicht auf das Ereignis reagiert, sollte false zurückgegeben werden, damit die anderen Komponenten im System die Chance erhalten, dieses Ereignis zu sehen. In den meisten Beispielen der heutigen Lektion werden Sie auf einfache Ereignisse reagieren, weshalb hier immer true zurückgegeben wird. Morgen erfahren Sie genaueres über das Verschachteln von Komponenten und das Weiterleiten von Ereignissen in der Komponentenhierarchie.
Die zweite Hälfte des Mausklicks ist die mouseUp-Methode, die aufgerufen wird, sobald die Maustaste losgelassen ist. Um ein mouseUp-Ereignis zu behandeln, fügen Sie die mouseUp()-Methode in das Applet ein: mouseUp()sieht genauso aus wie mouseDown():
public boolean mouseUp(Event evt, int x, int y) {
....
}
In diesem Abschnitt erstellen Sie ein Beispiel-Applet, das das Ereignismodell 1.02 zur Behandlung von Mausereignissen insbesondere mouseDown-Ereignisse verwendet. Das Spots-Applet beginnt mit einem leeren Bildschirm und bleibt dann ruhig abwartend. Wird die Maus in diesem Bildschirm geklickt, erscheint ein blauer Punkt. Sie können in diesem Bildschirm bis zu zehn Punkte anordnen. Abb. 11.1 zeigt das Spots-Applet.
Abbildung 11.1:
|
Wir erstellen dieses Applet nun von Anfang an. Beginnen Sie mit der Klassendefinition:
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
public class Spots extends java.applet.Applet {
final int MAXSPOTS = 10;
int xspots[] = new int[MAXSPOTS];
int yspots[] = new int[MAXSPOTS];
int currspots = 0;
}
In dieser Definition werden drei andere AWT-Klassen verwendet: Graphics, Color und Event. Event muß in jedes Applet importiert werden. Die Klasse hat vier Instanzvariablen: Eine Konstante, um die Höchstzahl der zu zeichnenden Punkte festzulegen, zwei Arrays, um die X- und Y-Koordinaten der bereits gezeichneten Punkte zu speichern und eine Ganzzahl, um die Nummer des aktuellen Punktes zu verfolgen.
Jetzt schreiben Sie die init()-Methode, die aus einer Zeile besteht, um als Hintergrundfarbe Weiß zu definieren:
public void init() {
setBackground(Color.white);
}
Der Hintergrund wird hier und nicht in paint() definiert, weil paint() wiederholt aufgerufen wird, sobald ein neuer Punkt hinzukommt. Da Sie den Hintergrund aber nur einmal einstellen möchten, würde eine Einbindung in die paint()-Methode den Ablauf verlangsamen.
Die Hauptaktion dieses Applet findet in der mouseDown()-Methode statt. Sie fügen jetzt eine solche Methode ein:
public boolean mouseDown(Event evt, int x, int y) {
if (currspots < MAXSPOTS) {
addspot(x,y);
return true;
}
else {
System.out.println("Too many spots.");
return false;
}
}
Findet der Mausklick statt, prüft die mouseDown()-Methode, ob weniger als zehn Punkte vorhanden sind. Trifft dies zu, ruft sie die addspot()-Methode (die Sie im Anschluß schreiben) auf und gibt true zurück (mouseDown-Ereignis wurde aufgegriffen und behandelt). Andernfalls wird eine Fehlermeldung aus und false zurückgegeben. Was bewirkt addspot()? Diese Methode fügt die Koordinaten des Punktes in die Arrays ein, in denen die Koordinaten gespeichert werden, erhöht die currspots-Variable und ruft dann repaint() auf:
void addspot(int x, int y) {
xspots[currspots] = x;
yspots[currspots] = y;
currspots++;
repaint();
}
Vielleicht fragen Sie sich, warum man neben dem aktuellen Punkt auch alle bereits aktivierten Punkte verfolgen muß. Der Grund liegt an repaint(): Jedesmal, wenn der Bildschirm neu gezeichnet wird, müssen zusätzlich zum letzten auch alle alten Punkte ausgegeben werden. Andernfalls würde immer nur jeweils der aktuelle Punkte ohne die alten Punkte erscheinen. Nun wenden wir uns der paint()-Methode zu:
public void paint(Graphics g) {
g.setColor(Color.blue);
for (int i = 0; i < currspots; i++) {
g.fillOval(xspots[i] -10, yspots[i] 10, 20, 20);
}
}
Innerhalb von paint stellen Sie die in den xspots- und yspots-Arrays gespeicherten Punkte in eine Schleife, damit einer nach dem anderen gezeichnet wird (leicht nach rechts oben gerückt, damit der Punkt rund um den Mauszeiger, nicht unterhalb rechts ausgegeben wird).
Das wars schon! Damit haben Sie ein Applet geschrieben, das Mausklicks behandelt. Den Rest überlasse ich Ihnen. Sie müssen das entsprechende Verhalten für mouseDown() oder mouseUp() einfügen, damit die Ereignisse abgewickelt werden. Den kompletten Code für das Spots-Applet finden Sie in Listing 11.1.
Listing 11.1: Das Spots-Applet
1: import java.awt.Graphics;
2: import java.awt.Color;
3: import java.awt.Event;
4:
5: public class Spots extends java.applet.Applet {
6:
7: final int MAXSPOTS = 10;
8: int xspots[] = new int[MAXSPOTS];
9: int yspots[] = new int[MAXSPOTS];
10: int currspots = 0;
11:
12: public void init() {
13: setBackground(Color.white);
14: }
15:
16: public boolean mouseDown(Event evt, int x, int y) {
17: if (currspots < MAXSPOTS) {
18: addspot(x,y);
19: return true;
20: }
21: else {
22: System.out.println("Too many spots.");
23: return false;
24: }
25: }
26:
27: void addspot(int x,int y) {
28: xspots[currspots] = x;
29: yspots[currspots] = y;
30: currspots++;
31: repaint();
32: }
33:
34: public void paint(Graphics g) {
35: g.setColor(Color.blue);
36: for (int i = 0; i < currspots; i++) {
37: g.fillOval(xspots[i] 10, yspots[i] 10, 20, 20);
38: }
39: }
40: }
Was ist zu tun, wenn Sie nicht nur an einfachen Mausklicks interessiert sind? Wie können Sie doppelte oder dreifache Mausklicks behandeln? Die Event-Klasse von Java enthält eine Variable namens clickCount zum Verfolgen dieser Informationen. ClickCount ist eine Ganzzahl, welche die Anzahl aufeinanderfolgender Mausklicks wiedergibt, die stattgefunden haben (was als aufeinanderfolgend interpretiert wird, ist meist im Betriebssystem oder der Maus-Hardware festgelegt). Wenn Sie in den Applets mehrere Mausklicks behandeln möchten, können Sie diesen Wert innerhalb der mouseDown()-Methode wie folgt testen:
public boolean mouseDown(Event evt, int x, int y) {
switch (evt.clickCount) {
case 1: // einzel-Klick
case 2: // doppel-Klick
case 3: // triple-Klick
....
}
}
Jedesmal, wenn die Maus um ein Pixel in eine Richtung bewegt wird, wird ein Mausbewegungsereignis erzeugt. Wir unterscheiden zwei Mausbewegungsereignisse: das Ziehen der Maus mit gedrückter Maustaste und die einfache Bewegung des Mauszeigers ohne Drücken einer Maustaste. Darüber hinaus werden weitere Ereignisse beim Eintritt des Mauszeigers in den Bereich des Applet bzw. bei dessen Verlassen erzeugt. (Wenn Sie sich fragen, wozu Sie diese Art Information benötigen, so sei hier bereits darauf hingewiesen, daß diese für AWT-Komponenten sinnvoll sein können, die Sie innerhalb eines Applet plazieren, doch über das AWT erfahren Sie morgen genaueres).
Für jedes dieser Ereignisse werden spezielle Methoden definiert, die diese Ereignisse aufgreifen, ebenso wie die Methoden mouseDown und mouseUp für die Intervention bei Mausklicks.
Um Mausbewegungsereignisse im Ereignismodell 1.02 zu behandeln, verwenden Sie die Methoden mouseDrag und mouseMove.
Die mouseMove()-Methode zur Behandlung einfacher Mausbewegungen ohne gedrückte Maustaste, ist den Mausklick-Methoden sehr ähnlich:
public boolean mouseMove(Event evt, int x, int y) {
...
}
Die mouseDrag()-Methode behandelt Mausbewegungen, die mit gedrückter Maustaste durchgeführt werden (eine komplette Ziehbewegung besteht aus einem mouseDown-Ereignis, einer Reihe von mouseDrag-Ereignissen für jeden Pixel, um die sich die Maus bewegt und einem mouseUp-Ereignis, wenn die Maustaste losgelassen wird.) Die mouseDrag-Methode sieht wie folgt aus:
public boolean mouseDrag(Event evt, int x, int y) {
...
}
Beachten Sie, daß die Argumente in mouseMove() und mouseDrag() die neue Mausposition kennzeichnen und nicht den Ausgangspunkt der Bewegung.
Die mouseEnter()- und mouseExit()-Methoden werden aufgerufen, wenn der Mauszeiger den Applet-Bereich »betritt« bzw. verläßt. Sowohl mouseEnter() als auch mouseExit() haben ähnliche Signaturen wie die Mausklick-Methoden. Sie verfügen über drei Argumente: Das Ereignisobjekt und die X- und Y-Koordinaten des Punktes, an dem der Mauszeiger über dem Applet-Bereich steht oder an dem er den Applet-Bereich verläßt. Die folgenden Beispiele zeigen die Unterschriften für mouseEnter() und mouseExit():
public boolean mouseEnter(Event evt, int x, int y) {
...
}
public boolean mouseExit(Event evt, int x, int y) {
...
}
Ein praktisches Beispiel ist immer hilfreich, um bestimmte Sachverhalte zu verstehen. In diesem Abschnitt schreiben Sie ein Applet, mit dem gerade Linien am Bildschirm durch Ziehen der Maus vom Anfangs- zum Endpunkt erstellt werden. Das ausgeführte Applet ist in Abb. 11.2 dargestellt.
Abbildung 11.2:
|
Wie beim Spots-Applet (auf dem dieses Applet basiert) beginnen Sie mit der Definition und arbeiten dann die einzelnen Schritte durch, indem Sie die betreffenden Methoden für das Applet hinzufügen. Im folgenden finden Sie die Klassendefinition für das Lines-Applet, mit einigen Instanzvariablen und einer einfachen init()-Methode:
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Point;
public class Lines extends java.applet.Applet {
final int MAXLINES = 10;
Point starts[] = new Point[MAXLINES]; // Startpunkte
Point ends[] = new Point[MAXLINES]; // Endpunkte
Point anchor; // Start der aktuellen Linie
Point currentpoint; // Ende der aktuellen Linie
int currline = 0; // Anzahl der Linien
public void init() {
setBackground(Color.white);
}
}
Dieses Applet verfügt im Vergleich zum Spots-Applet über einige Zusätze. Im Gegensatz zu Spots, das die einzelnen ganzzahligen Koordinaten verfolgt, werden hier Point-Objekte verfolgt. Diese Punkte stellen eine X- und Y-Koordinate dar, die in einem Objekt eingekapselt ist. Um Punkte zu handhaben, importieren Sie die Point-Klasse (java.awt.Point) und richten eine Reihe von Instanzvariablen ein, die diese Punkte enthalten:
In der init()-Methode wird schließlich festgelegt, daß der Hintergrund des Applet weiß sein soll (wie auch in Spots).
Die drei wichtigen Ereignisse dieses Applet sind mouseDown(), um den Ankerpunkt für die aktuelle Linie zu setzen, mouseDrag(), um die aktuelle Linie während des Zeichnens zu animieren und mouseUp(), um den Endpunkt für die neue Linie zu definieren. Da Sie über Instanzvariablen für diese Werte verfügen, müssen Sie nur noch die richtigen Variablen in die richtigen Methoden einfügen. Im folgenden setzen Sie mit mouseDown() den Ankerpunkt (aber nur wenn die maximale Anzahl der Linien nicht überschritten wurde):
public boolean mouseDown(Event evt, int x, int y) {
if (currline < MAXLINES) {
anchor = new Point(x,y);
return true;
}
else {
System.out.println("Too many lines.");
return false;
}
}
Während die Maus gezogen wird, um die Linie zu zeichnen, animiert das Applet die momentan gezeichnete Linie. Durch Ziehen der Maus bewegt sich die neue Linie vom Ankerpunkt zur Spitze des Mauszeigers mouseDrag()-Ereignis enthält den jeweils aktuellen Punkt, auf dem sich die Maus bewegt, deshalb wird diese Methode benutzt, um den aktuellen Punkt zu verfolgen (und bei jeder Bewegung nachzuzeichnen, um die Linie zu »animieren«). Falls Sie die maximale Linienanzahl überschritten haben, läßt sich diese Arbeit nicht ausführen. Im folgenden finden Sie die mouseDrag()-Methode, welche alle diese Aufgaben ausführt:
public boolean mouseDrag(Event evt, int x, int y) {
if (currline < MAXLINES) {
currentpoint = new Point(x,y);
repaint();
return true;
}
else return false;
}
Die neue Linie wird erst beim Loslassen der Maustaste in die Arrays der alten Linien aufgenommen. Hier wird mit mouseUp() sichergestellt, daß die Höchstzahl der Linien nicht überschritten wurde, bevor die addline()-Methode (wird weiter unten beschrieben) aufgerufen wird:
public boolean mouseUp(Event evt, int x, int y) {
if (currline < MAXLINES) {
addline(x,y);
return true;
}
else return false;
}
In der addline()-Methode werden die Arrays für Anfangs- und Endpunkte aktualisiert und das Applet wird nachgezeichnet, um die jeweils neue Linie zu berücksichtigen:
void addline(int x,int y) {
starts[currline] = anchor;
ends[currline] = new Point(x,y);
currline++;
currentpoint = null;
anchor = null;
repaint();
}
Beachten Sie, daß in dieser Methode auch currentpoint und anchor auf null gesetzt werden. Warum? Weil der Zeichenvorgang für die aktuelle Linie beendet ist. Indem Sie diese Variablen auf null setzen, können Sie diesen Wert in der paint()-Methode prüfen, um herauszufinden, ob Sie eine aktuelle Linien zeichnen müssen.
Die Ausgabe des Applet bedeutet, daß alle alten Linien, die in den starts- und ends-Arrays gespeichert sind, zusätzlich zur jeweils aktuellen Linie (deren Endpunkte in anchor bzw. currentpoint stehen) gezeichnet werden. Um die Animation der aktuellen Linie für den Benutzer gut sichtbar darzustellen, wird sie in Blau ausgegeben. Im folgenden finden Sie die paint()-Methode für das Lines-Applet.
public void paint(Graphics g) {
// Bestehende Linien zeichnen
for (int i = 0; i < currline; i++) {
g.drawLine(starts[i].x, starts[i].y,
ends[i].x, ends[i].y);
}
// Aktuelle Linie zeichnen
g.setColor(Color.blue);
if (currentpoint != null)
g.drawLine(anchor.x, anchor.y,
currentpoint.x, currentpoint.y);
}
Wenn Sie jeweils die aktuelle Linie in paint einbinden, können Sie vorab testen, ob currentpoint einen Wert von verschieden null hat. In diesem Fall wird gerade keine Linie im Applet gezeichnet, deshalb besteht kein Grund, eine Linie auszugeben, die nicht existiert. Durch Testen von currentpoint (und indem currentpoint in der addline()-Methode auf null gesetzt wird), können Sie die Ausgabe auf das beschränken, was nötig ist. Mit nur 60 Codezeilen und einigen einfachen Methoden haben Sie eine einfache Zeichenanwendung für den Web-Browser entwickelt. Listing 11.2 zeigt den kompletten Text des Lines-Applet, in dem die einzelnen Teile zusammengefaßt werden.
Listing 11.2: Das Lines-Applet
1: import java.awt.Graphics;
2: import java.awt.Color;
3: import java.awt.Event;
4: import java.awt.Point;
5:
6: public class Lines extends java.applet.Applet {
7:
8: final int MAXLINES = 10;
9: Point starts[] = new Point[MAXLINES]; // Startpunkte
10: Point ends[] = new Point[MAXLINES]; // Endpunkte
11: Point anchor; // Start der aktuellen Linie
12: Point currentpoint; // Ende der aktuellen Linie
13: int currline = 0; // Anzahl der Linien
14:
15: public void init() {
16: setBackground(Color.white);
17: }
18:
19: public boolean mouseDown(Event evt, int x, int y) {
20: if (currline < MAXLINES) {
21: anchor = new Point(x,y);
22: return true;
23: }
24: else {
25: System.out.println("Too many lines.");
26: return false;
27: }
28: }
29:
30: public boolean mouseUp(Event evt, int x, int y) {
31: if (currline < MAXLINES) {
32: addline(x,y);
33: return true;
34: }
35: else return false;
36: }
37:
38: public boolean mouseDrag(Event evt, int x, int y) {
39: if (currline < MAXLINES) {
40: currentpoint = new Point(x,y);
41: repaint();
42: return true;
43: }
44: else return false;
45: }
46:
47: void addline(int x,int y) {
48: starts[currline] = anchor;
49: ends[currline] = new Point(x,y);
50: currline++;
51: currentpoint = null;
52: anchor = null;
53: repaint();
54: }
55:
56: public void paint(Graphics g) {
57:
58: // Bestehende Linien zeichnen
59: for (int i = 0; i < currline; i++) {
50: g.drawLine(starts[i].x, starts[i].y,
51: ends[i].x, ends[i].y);
52: }
53:
54: // Aktuelle Linie zeichnen
55: g.setColor(Color.blue);
56: if (currentpoint != null)
57: g.drawLine(anchor.x,anchor.y,
58: currentpoint.x,currentpoint.y);
59: }
60:}
Ein Tastaturereignis wird erzeugt, wenn ein Benutzer eine Taste auf der Tastatur drückt. Wenn Sie Tastaturereignisse verwenden, können Sie die Werte jener Tasten einholen, die der Benutzer zur Durchführung einer Aktion gedrückt hat oder vom den Benutzern Ihres Applet eine Zeicheneingabe erhalten.
Um ein Tastaturereignis im Ereignismodell 1.02 zu erfassen, verwenden Sie die Methode keyDown():
public boolean keyDown(Event evt, int key) {
...
}
Die Tasten, die von den keyDown-Ereignissen erzeugt und als Tastenargument in keyDown() weitergeleitet werden, sind Ganzzahlen, die eindeutige Zeichenwerte darstellen, zu denen sowohl die alphanumerischen Zeichen, als auch die Funktionstasten, Tabstopps, Absatzzeichen usw. gehören. Wenn Sie diese als Zeichen verwenden möchten (etwa für die Ausgabe), müssen Sie diese wie folgt als Zeichen definieren:
currentchar = (char)key;
Im folgenden finden Sie ein einfaches Beispiel für eine keyDown()-Methode, die im Grunde lediglich jene Taste zurückgibt, deren Unicode und Zeichendarstellungen Sie soeben eingegeben haben (es macht Spaß zu beobachten, welche Tasten, welche Werte erzeugen):
public boolean keyDown(Event evt, int key) {
System.out.println("ASCII value: " + key);
System.out.println("Character: " + (char)key);
return true;
}
Ebenso wie bei Mausklicks verfügt auch jedes keyDown-Ereignis über ein entsprechende keyUp-Ereignis. Um keyUp-Ereignisse zu erfassen, verwenden Sie die folgende keyUp()-Methode:
public booklean keyUp(Event evt, int key) {
...
}
Die Event-Klasse bietet verschiedene Klassenvariablen, die sich auf mehrere nicht alphanumerische Standardtasten, z.B. die Pfeiltasten, beziehen. Werden diese Tasten in Ihrem Applet benutzt, können Sie den Code übersichtlicher gestalten, indem Sie in Ihrer keyDown()-Methode für diese Tasten Namen anstelle von numerischen Werten verwenden. Um z.B. zu prüfen, ob die PfeilOben-Taste gedrückt wurde, können Sie folgenden Code schreiben:
if (key == Event.UP) {
...
}
Da diese Klassenvariablen ganzzahlige Werte enthalten, können Sie auch die switch-Anweisung verwenden, um diese zu testen.
In Tabelle 11.1 finden Sie Standardvariablen der Event-Klasse für verschiedene Tasten.
Tabelle 11.1: Standardtasten, die in der Event-Klasse definiert sind.
Klassenvariable |
Taste |
Event.HOME |
Pos 1 |
Event.END |
Ende |
Event.PGUP |
Bild auf |
Event.PGDN |
Bild an |
Event.UP |
Pfeil oben |
Event.DOWN |
Pfeil unten |
Event.LEFT |
Pfeil links |
Event.RIGHT |
Pfeil rechts |
Event.F1 |
F1 |
Event.F2 |
F2 |
Event.F3 |
F3 |
Event.F4 |
F4 |
Event.F5 |
F5 |
Event.F6 |
F6 |
Event.F7 |
F7 |
Event.F8 |
F8 |
Event.F9 |
F9 |
Event.F10 |
F10 |
Event.F11 |
F11 |
Event.F12 |
F12 |
Im folgenden wird ein Applet erläutert, das Tastaturereignisse im Ereignismodell 1.02 demonstriert. Bei diesem Applet kann man ein Zeichen auf der Tastatur eingeben, das dann in der Mitte des Applet-Fensters angezeigt wird. Anschließend läßt sich das Zeichen mit den Pfeiltasten am Bildschirm versetzen. Durch Eingabe eines weiteren Zeichens ändert sich das aktuell angezeigte Zeichen. Abb. 11.3 zeigt ein Beispiel hierfür.
Abbildung 11.3:
|
Dieses Applet ist weniger komplex als die zuvor erläuterten Applets. Es verfügt über nur drei Methoden: init(), keyDown() und paint(). Die Instanzvariablen sind ebenfalls einfacher, weil Sie lediglich die X- und Y-Positionen der aktuellen Zeichen und der Werte der betreffenden Zeichen verfolgen müssen. Zunächst wieder die Klassendefinition:
import java.awt.Graphics;
import java.awt.Event;
import java.awt.Font;
import java.awt.Color;
public class Keys extends java.applet.Applet {
char currkey;
int currx;
int curry;
}
Die init()-Methode ist für drei Aufgaben zuständig: Definition der Hintergrundfarbe, Einrichten der Applet-Schriftarten (in diesem Fall 36-Punkt Helvetica fett) und Festlegung der Anfangsposition für das Zeichen (Bildschirmmitte abzüglich einiger Punkte für ein leichtes Versetzen des Zeichens nach rechts oben).
public void init() {
currx = (size().width / 2) 8;
curry = (size().height / 2) 16;
setBackground(Color.white);
setFont(new Font("Helvetica", Font.BOLD, 36));
}
Da das Verhalten des Applet auf Tastatureingaben basiert, findet die Hauptarbeit des Applet in der keyDown()-Methode statt:
public boolean keyDown(Event evt, int key) {
switch (key) {
case Event.DOWN:
curry += 5;
break;
case Event.UP:
curry -= 5;
break;
case Event.LEFT:
currx -= 5;
break;
case Event.RIGHT:
currx += 5;
break;
default:
currkey = (char)key;
}
repaint();
return true;
}
In der Mitte des keyDown()-Applet befindet sich ein switch-Anweisung, die auf verschiedene Tastenereignisse testet. Ist das Ereignis eine Pfeiltaste, wird die Position des Zeichens entsprechend geändert. Ist das Ereignis eine andere Taste, ändert sich das Zeichen selbst (dies ist der Standardteil von switch). Die Methode endet mit repaint() und gibt true aus.
Die paint()-Methode ist hier fast inhaltslos. Es wird lediglich das aktuelle Zeichen an der aktuellen Position angezeigt. Beachten Sie aber, daß es beim ersten Starten des Applet kein Anfangszeichen gibt und nichts zu zeichnen ist. Das muß berücksichtigt werden. Die Variable currkey wird auf 0 initialisiert, so daß das Applet nur gezeichnet wird, wenn currkey einen tatsächlichen Wert hat:
public void paint(Graphics g) {
if (currkey != 0) {
g.drawString(String.valueOf(currkey), currx,curry);
}
}
Listing 11.3 zeigt den kompletten Quellcode für das Keys-Applet.
Listing 11.3: Das Keys-Applet
1: import java.awt.Graphics;
2: import java.awt.Event;
3: import java.awt.Font;
4: import java.awt.Color;
5:
6: public class Keys extends java.applet.Applet {
7:
8: char currkey;
9: int currx;
10: int curry;
11:
12: public void init() {
13: currx = (size().width / 2) -8; // Standard
14: curry = (size().height / 2) -16;
15:
16: setBackground(Color.white);
17: setFont(new Font("Helvetica",Font.BOLD,36));
18: }
19:
20: public boolean keyDown(Event evt, int key) {
21: switch (key) {
22: case Event.DOWN:
23: curry += 5;
24: break;
25: case Event.UP:
26: curry -= 5;
27: break;
28: case Event.LEFT:
29: currx -= 5;
30: break;
31: case Event.RIGHT:
32: currx += 5;
33: break;
34: default:
35: currkey = (char)key;
36: }
37:
38: repaint();
39: return true;
40: }
41:
42: public void paint(Graphics g) {
43: if (currkey != 0) {
44: g.drawString(String.valueOf(currkey), currx,curry);
45: }
46: }
47: }
Zu den sogenannten Modifizierungstasten zählen die Umschalttaste (Umsch), Steuerungstaste (Strg) und Meta. Sie erzeugen selbst keine Tastenereignisse, aber wenn Sie ein gewöhnliches Maus- oder Tastaturereignis erhalten, können Sie testen, ob eine dieser Tasten gedrückt wurde, während das Ereignis stattfand. In manchen Fällen ist das offensichtlich. Alphanumerische Tasten, die gleichzeitig mit der Umschalttaste gedrückt werden, erzeugen z.B. andere Zeichen als ohne. In anderen Fällen, insbesondere zusammen mit der Maus, soll ein Ereignis mit einer gedrückten Modifizierungstasten aktiviert werden, um es von der üblichen Version des jeweiligen Ereignisses zu unterscheiden.
Die Event-Klasse enthält drei Methoden, um zu testen, ob eine Taste gleichzeitig mit einer Ergänzungstaste gedrückt wurde: shiftDown(), metaDown() und controlDown(). Alle drei geben boolesche Werte zurück, die Auskunft darüber geben, ob die jeweilige Modifizierungstaste gedrückt wurde. Sie können diese drei Methoden in jeder beliebigen Ereignisbehandlung (Maus oder Tastatur) verwenden, indem Sie diese zu jenem Ereignisobjekt aufrufen, das an die jeweilige Methode weitergegeben wurde.
public boolean mouseDown(Event evt, int x, int y ) {
if (evt.shiftDown())
// Shift-Klick verarbeiten
else if controlDown()
/// Steuerung-Klick verarbeiten
else // Normalen Klick verarbeiten
}
Ein weiterer wichtiger Verwendungszweck der Modifizierungstasten-Methoden besteht darin, zu testen, welche Maustaste ein spezielles Mausereignis erzeugt hat dies gilt für Systeme mit zwei oder drei Maustasten. Standardmäßig werden Mausereignisse (wie das Drücken und Ziehen) im Ereignismodell 1.02 unabhängig davon erzeugt, welche Maustaste benutzt wurde. Doch die Java-Ereignisse zeichnen interne Aktionen der linken oder mittleren Maustaste zusammen mit den Ergänzungstasten Meta und Steuerung (Strg) auf, d.h., ein Test auf die Tasten prüft die Maustastenaktion. Indem Sie die Modifizierungstasten testen, können Sie ermitteln, welche Maustaste gedrückt wurde und für die jeweiligen Maustasten verschiedene Verhaltensweisen definieren. Hiermit läßt sich also nicht nur die linke Maustaste mit Ereignissen versehen. Verwenden Sie für diesen Test eine if-Anweisung wie folgt:
public boolean mouseDown(Event evt, int x, int y ) {
if (evt.metaDown())
// einen Klick mit der rechten Maustaste verarbeiten
else if (evt.controlDown())
// einen Klick mit der mittleren Maustaste verarbeiten
else // einen normalen Klick verarbeiten
}
Da diese Aufzeichnung mehrerer Maustasten in Modifizierungstasten automatisch vorgenommen wird, müssen Sie nicht viel tun, um sicherzustellen, daß ein Applet oder eine Anwendung auch auf anderen Systemen mit anderen Mäusen funktioniert. Da die linken oder rechten Mausklicks in Modifizierungstasten-Ereignissen aufgezeichnet werden, können Sie die tatsächlichen Modifizierungstasten auf einem System mit wenig Maustasten für exakt dieselben Ereignisse verwenden. Zum Beispiel: Das Drücken der Steuerungstaste und gleichzeitig Klicken mit der Maus unter Windows oder das Drücken der Control-Taste auf einem Macintosh entspricht genau dem Klicken der mittleren Maustaste auf einem Maussystem mit drei Tasten. Wenn Sie die Command-Taste (Apple) drücken und mit der Maus auf einem Macintosh klicken, ist diese identisch mit dem Klicken der rechten Maustaste auf einem Maussystem mit zwei oder drei Tasten.
Bedenken Sie jedoch, daß die Verwendung von verschiedenen Maustasten oder Modifizierungstasten eventuell nicht sofort deutlich wird, wenn das Applet oder die Anwendung auf einem System ausgeführt wird, das weniger Tasten zur Verfügung stellt, als bei Ihnen üblich. Sie können entweder die Oberfläche auf die Verwendung einer einzigen Maustaste beschränken oder in Form einer Hilfe oder Dokumentation darlegen, wie Ihr Programm in diesem Fall benutzt werden soll.
Die Standardmethoden, die Sie heute zur Behandlung von grundlegenden Ereignissen kennengelernt haben, werden von einem generischen Event-Handler aufgerufen, einer Methode mit dem Namen handleEvent(). Die handleEvent()-Methode spiegelt die Art wider, in der AWT 1.02 im allgemeinen mit Ereignissen umgeht, die zwischen Anwendungskomponenten und Ereignissen aufgrund von Benutzereingaben stattfinden.
In der Standardmethode handleEvent() werden Ereignisse verarbeitet und die Methoden, die Sie heute gelernt haben, werden aufgerufen. Um die hier beschriebenen Standardereignisse zu ändern oder eigene Ereignisse zu definieren und weiterzugeben, müssen Sie handleEvent in Ihrem Java-Programm überschreiben. Die handleEvent()-Methode sieht wie folgt aus:
public boolean handleEvent(Event evt) {
...
}
Um spezielle Ereignisse zu testen, prüfen Sie die ID-Instanzvariable des Event-Objekts, das darin weitergegeben wird. Die Ereignis-ID ist eine Ganzzahl, jedoch definiert die Event-Klasse glücklicherweise eine ganze Reihe von Ereignis-IDs als Klassenvariablen, die Sie im Körper von handleEvent testen können. Da diese Klassenvariablen ganzzahlige Konstanten sind, eignet sich eine switch-Anweisung besonders gut. Die folgende handleEvent()-Methode ist ein einfaches Beispiel bei dem Informationen über Mausereignisse ausgegeben werden:
public boolean handleEvent(Event evt) {
switch (evt.id) {
case Event.MOUSE_DOWN:
System.out.println("MouseDown: " +
evt.x + "," + evt.y);
return true;
case Event.MOUSE_UP:
System.out.println("MouseUp: " +
evt.x + "," + evt.y);
return true;
case Event.MOUSE_MOVE:
System.out.println("MouseMove: " +
evt.x + "," + evt.y);
return true;
case Event.MOUSE_DRAG:
System.out.println("MouseDrag: " +
evt.x + "," + evt.y);
return true;
default:
return false;
}
}
Sie können auf die folgenden Tastaturereignisse testen:
Ferner können Sie auf folgende Mausereignisse testen:
Abgesehen von diesen Ereignissen enthält die Event-Klasse eine ganze Reihe von Methoden zur Behandlung von 1.02-AWT-Komponenten der Benutzeroberfläche. Sie lernen diese Methode morgen.
Beachten Sie ferner, daß handleEvent() ebenso wie die individuellen Methoden für die einzelnen Ereignisse, einen booleschen Wert ausgibt. Der hier zurückgegebene Wert ist besonders wichtig; wenn Sie die Ereignisbehandlung an eine andere Methode weitergeben, müssen Sie false zurückgeben (die aufgerufene Methode selbst wird true oder false zurückgeben). Wenn die Behandlung des Ereignisses im Rumpf diese Methode ausgeführt wird, geben Sie true zurück. Wird das Ereignis an eine Superklasse weitergeleitet, gibt diese Methode automatisch true oder false zurück, d.h. in diesem Fall müssen Sie dies nicht selbst zurückgeben.
Sie wissen nun, wie sich Maus- und Tastaturereignisse in alten JDK Ereignismodell 1.02 behandeln lassen und können jetzt damit beginnen, dies alles noch einmal für das neue JDK Ereignismodell 1.1 zu lernen. Wie bereits erwähnt, müssen Sie in dieser Lektion nicht bereits mit dem neuen Ereignismodell 1.1 arbeiten, sofern Sie nicht ausschließlich in einer 1.1-Umgebung arbeiten. Doch ist es ratsam, z.B. wenn Sie wissen möchten, wie sich alte Codes in neue konvertieren lassen, sich diesem Thema zumindest einmal zu nähern, denn dieses Modell wird zweifellos die Zukunft sein.
Auch wenn das Modell ein anderes ist, lassen sich doch viele Kenntnisse, die Sie bereits zum alten Ereignismodell in den letzten Abschnitten erhalten haben, für das Ereignismodell 1.1 verwenden. Ein MouseDown ist auch hier ein MouseDown, der wesentliche Unterschied zwischen den Ereignismodellen besteht vor allem in der Art, in der Ereignisse durch das System »geschleust« werden und wie das Applet programmiert werden muß, um diese Ereignisse behandeln zu können.
In diesem Abschnitt lernen Sie das neue Modell kennen, Sie erfahren, warum dieses Modell notwendig wurde und wie es funktioniert. Darüber hinaus werden Sie das Lines-Applet vom alten Modell in das neue konvertieren, damit Sie auch von diesem Vorgang einen Eindruck erhalten.
Warum wird eigentlich überhaupt mit zwei verschiedenen Modellen gearbeitet? Was war an dem alten Ereignismodell 1.02 so schlecht, warum mußte es so drastisch geändert werden?
Das alte Ereignismodell ist nicht grundsätzlich schlecht, es ist einfach und leicht zu lernen und funktioniert für kleinere Java-Anwendungen wie Applet gut, die über nur einige Oberflächenelemente verfügen und nur einige Ereignisse behandeln müssen. Wenn Sie jedoch mit größeren und umfangreicheren Anwendungen arbeiten, in denen z.B. Hunderte von Ereignissen stattfinden können, die jeweils auf unterschiedliche Art verarbeitet werden müssen, gelangt das alte Modell an seine Grenzen und wird unübersichtlich. Im alten Modell lassen sich auch nur schwer auf Komponenten basierende Systeme entwickeln, welche die JavaBeans vorsehen.
Im neuen Ereignismodell 1.1 sind einige Beschränkungen des Ereignismodell 1.02 aufgehoben. Die erste Beschränkung besteht im Problem des handleEvent(). Wie Sie im obigen Abschnitt zu diesem Thema erfahren haben, wird die handleEvent()-Methode dazu benutzt, alle Ereignisse zu verarbeiten, die für Ihr Applet sinnvoll sein könnten. Das ist keine sehr sinnvolle Art der Ereignisverarbeitung. Im Ereignismodell 1.1 wird diese Beschränkung insofern aufgehoben, als nur noch jene Ereignisse zum Applet gesendet werden, die für das Applet von Interesse sind. Dieser Vorgang erfordert vom Applet bei weitem weniger Arbeit, um Ereignisse zu behandeln.
Die zweite Beschränkung des Ereignismodells 1.02 besteht in der Darstellungsform von Ereignissen. In 1.02 sind alle Ereignisse Instanzen der Event-Klasse. Ein Ereignis-ID identifiziert die Art des Ereignisses eines Objekts. Um zu entscheiden, was in einem Ereignis ausgeführt werden soll, müssen Sie eine große switch-Anweisung innerhalb von handleEvent()verwenden, um festzulegen, welche Art von Ereignissen verwendet werden sollen und diese dann behandeln bzw. sie an eine Methode wie mouseDown()weiterzuleiten. Und was geschieht, wenn Sie über mehrere Ereignisse mit demselben ID verfügen, die aber von verschiedenen Oberflächen-Elementen erzeugt wurden (z.B. von verschiedenen Schaltflächen in einem Applet)? Dann sind weitere switch-Anweisungen erforderlich oder Sie müssen zusätzliche if...else-Anweisungen entwickeln. Je größer das Programm wird, desto komplizierter wird die Ereignisbehandlung und desto schneller kommt es zu Fehlern. Das Ereignismodell 1.1 behebt dieses Problem, indem es die Ereignisse in Klassen unterteilt und für jede dieser verschiedenen Ereignisklassen eigene Listener für die Behandlung verwendet. Es unterteilt also die Logik in überschaubare Unterbereiche, die sich jeweils separat entwickeln lassen.
Das dritte Problem des Ereignismodells 1.02 bestand in der Vererbbarkeit. Um ein Ereignisverhalten einem AWT-Objekt hinzuzufügen, muß dieses Objekt für das Überschreiben von handleEvent() oder einer seiner Hilfsmethoden wie mouseDown() in eine Subklasse gelegt werden. Die ist bei Maus- und Tastaturereignissen nicht von großer Bedeutung, da für die Applet-Klasse bereits eine Subklasse definiert ist, um neben den Ereignissen auch andere Verhalten hinzufügen zu können.
Stellen Sie sich einmal eine Schaltfläche vor (ein Oberflächen-Element, das morgen noch genauer erläutert wird). Die Schaltfläche verfügt über ein äußeres Erscheinungsbild und über eine bestimmte Funktion. Sie müssen lediglich definieren, was geschehen soll, sobald ein Benutzer eine Schaltfläche anklickt. Bei dem alten Modell bestand die einzige Möglichkeit, diese Schaltfläche mit Funktionalität auszustatten, darin, die Button-Klasse mit einer Subklasse zu versehen. Für jede Schaltfläche auf der Oberfläche mußte deshalb entweder eine eigene Subklasse erstellt oder eine einzige Subklasse mit einer besonders langen Ereignismethode versehen werden, die den Namen des Button bei der Entscheidung über die Ausführung nicht mehr berücksichtigte. Diese Methode ist nicht gerade elegant und folgt auch nicht der guten objektorientierten Designtheorie, die eine Definition für Subklassen eigentlich nur bei wichtigen, wiederverwendbaren Änderungen an einer Klasse empfiehlt. Das Ereignismodell 1.1 vermeidet dieses Problem, indem der Ereigniscode insgesamt in eine eigenen Klasse versetzt wird. Er läßt sich dann von mehreren generischen Schaltflächen-Objekten verwenden bzw. wieder verwenden oder auch unabhängig von der Oberfläche selbst verändern.
Sie haben nun erfahren, warum das alte Modell nicht ideal ist, im folgenden lernen Sie, wie das neue Ereignismodell 1.1 aussieht und wie es sich verwenden läßt.
Der größte Unterschiede zwischen dem alten Ereignismodell 1.02 und dem neuen Ereignismodell 1.1 besteht darin, daß die Verarbeitung von Ereignissen in zwei getrennten Hauptbereichen stattfindet. Der erste Teil ist das Objekt, welches ein bestimmtes Ereignis empfängt dabei kann es sich um das Applet oder einen Teil des Applet, z.B. eine Schaltfläche, handeln.
Der zweite, wichtigere Teil ist der sogenannte Ereignis-Listener. Der Listener stellt einen bestimmten Satz von Ereignissen dar etwa ein Maus-Listener oder ein Tasten-Listener, ein Listener für Bildlaufleisten usw. Jeder Listener ist für eine bestimmte Art von Ereignissen zuständig. Wenn Sie einen Listener erstellen, fügen Sie den Ereigniscode in den betreffenden Listener ein.
Der Ereignisempfänger und der Listener sind durch die Listener-Registrierung miteinander verbunden. Um einen Listener für ein Programm zu registrieren, verwenden Sie eine spezielle Methode in Ihrem Applet-Code, die besagt: »Dieser Listener wird jene Ereignisse behandeln«. Im alten Modell empfing das Applet (der Ereignisempfänger) alle Ereignisse, die das System erzeugte. Im neuen Modell empfängt das Applet nur jene Ereignisse, für die ein Listener registriert wurde. Auf diese Weise läßt sich der gesamte Verarbeitungsvorgang sowohl innerhalb des Systems als auch innerhalb des Applet effizienter gestalten. Es werden also nicht mehr konstant alle Ereignisse geprüft, die stattfinden.
Abbildung 11.4 zeigt die Unterschiede zwischen den Ereignismodellen 1.02 und 1.1. Beachten Sie, daß im Modell 1.02 alle Ereignisse von Ihrem Applet behandelt werden, während die Ereignisse im Modell 1.1 besser verteilt werden.
Abbildung 11.4:
|
Es folgt ein dritter Teil, auf den sich dieses Modell stützt: Die Ereignisse selbst unterscheiden sich im alten und im neuen Modell. Wie bereits erwähnt, waren alle Ereignisse des Ereignismodells 1.02 Instanzen der Event-Klasse. Im Ereignismodell 1.1 steht ein gesamtes Paket mit Ereignissen in java.awt.events zur Verfügung, wobei die verschiedenen Klassen verschiedene Arten von Ereignissen darstellen. Dadurch werden die Ereignisse deutlich objektorientierter, sie können Ereignisinformationen von Ereignis-Superklassen erben und sie lassen sich an nur jene Listener oder den Listener-Code weiterleiten, der für diese Art von Ereignissen bestimmt ist.
Wenn Sie sich an die Einführung erinnern, habe ich dort das Bild eines Fließbands verwendet, um zu erklären, wie Ereignisse funktionieren. Im Modell 1.02 stehen Sie und das Applet am Fließband und beobachten die vorbeiziehenden Ereignisse, prüfen diese, um zu sehen, ob das eine oder andere für Ihre Zwecke sinnvoll ist. Im Ereignismodell 1.1 stehen mehrere Fließbänder zur Verfügung, auf denen jeweils verschiedene Ereignisarten transportiert werden. Sie und Ihr Applet haben Hilfskräfte (Listeners), die an den Fließbändern stehen, und die Ereignisse für Sie behandeln.
Haben Sie das Konzept verstanden? Im folgenden sollen einiges an Code geschrieben werden, um das ganze zu verdeutllichen. Wenn Sie das neue Ereignismodell in Ihren Applets oder Anwendungen benutzen, sollten sie die folgenden drei Grundschritte einhalten:
Im folgenden werden diese Schritte durchgearbeitet, damit Sie einen Eindruck von diesem Vorgang erhalten. Später soll dann ein Applet vom alten in das neue Modell konvertiert werden. Auch hierbei erhalten Sie noch einmal eine Vergleichsmöglichkeit und einen Einblick in die Funktionsweise des Ereignismodells 1.1.
Der erste Schritt besteht darin, herauszufinden, welche Ereignisse von Ihrem Applet benutzt werden sollen. Sie mußten diesen Schritt auch im Ereignismodell 1.02 ausführen und bestimmen, welche Methode (mouseDown(), keyDown(), handle-Event()) verwendet werden soll. Im Modell 1.1 ist dieser Vorgang jedoch etwas eindeutiger. Im Ereignismodell 1.1 werden die verschiedenen Ereignisse von verschiedenen Listenern bewältigt, die verschiedene Methoden verwenden. Wenn Ihr Applet also mouseDown-Ereignisse verwendet, müssen Sie den Listener suchen, der die mouseDown-Ereignisse unterstützt und eine Methode für die Behandlung dieser Ereignisse enthält.
Die jeweiligen Listener werden durch verschiedene Schnittstellen im Paket java.awt.event definiert. Wenn Sie die API-Dokumentation oder die Quelle für diese Schnittstellen durchsehen, werden Sie schnell herausfinden, welche Listener-Schnittstellen zu welchen Ereignissen und welchen Methoden in diesen Schnittstellen gehören (morgen und übermorgen erhalten Sie weitere Informationen zu den Ereignissen und den verfügbaren Listenern).
Heute soll der Schwerpunkt auf den Maus- und Tastatur-Ereignissen liegen. Für diese Ereignisse stehen drei Listener-Schnittstellen zur Verfügung. Tabelle 11.2 zeigt diejenigen Ereignisse und Methoden, welche in den drei Maus- und Tastatur-Listenern enthalten sind.
Tabelle 11.2: Schnittstellen und die von ihnen definierten Methoden
Notieren Sie sich die Listener-Namen, die Sie für Ihr Applet oder Ihre Anwendung verwenden möchten; Sie benötigen den Namen im nächsten Schritt. Über die Methoden müssen Sie sich momentan noch keine Gedanken machen (es genügt, wenn Sie gemerkt haben, daß sich die Namen und Unterschriften dieser Methoden von der Version 1.02 unterscheiden). Mit den Methoden arbeiten Sie im nächsten Schritt.
Im nächsten Schritt Sie wissen nun, welcher Listener die Ereignisse behandeln soll erstellen Sie den Listener. Der Listener enthält den Code, den Sie für die Behandlung der betreffenden Ereignisse schreiben.
Ein Ereignis-Listener ist technisch als Klasse definiert, die eine oder mehrere Listener-Schnittstellen implementiert. Gestern haben Sie erfahren, wie die Schnittstelle Runnable in Ihre Klassen eingefügt wird. Das Implementieren von Listenern erfolgt auf dieselbe Weise. Wenn Sie eine Listener-Klasse erstellt haben, enthält diese Klasse das Schlüsselwort implements in der Definition. Ferner wird der Name des speziellen Listenertyps für jenes Ereignis angegeben, das diese Klasse behandelt.
Wenn Sie eine Listener erstellen, können Sie dafür entweder eine nagelneue Klasse anlegen, welche die Ereignisse für das Haupt-Applet behandelt oder Sie ändern das Applet so ab, daß es sein eigener Listener ist. Die erste Variante ist die elegantere und vor allem für umfangreiche Programme mit vielen Ereignissen sinnvoll. Die zweite Technik bietet eine schnelle Möglichkeit, alte Ereigniscodierungen in neue zu konvertieren oder einfache Applets zu erstellen z.B. die Applets aus dieser Lektion.
Um eine eigene Listener-Klasse zu erstellen, können Sie entweder die Schnittstelle, die Sie im vorherigen Schritt herausgesucht haben, implementieren oder Sie verwenden eine abkürzende Technik: Sie können die Listener-Klasse so definieren, daß diese von einem Ereignis-Adapter erbt. Die Adapter sind Klassen aus dem Paket java.awt.event. Hier ist eine Klasse pro Listener-Schnittstelle vorgesehen, die jeweils einfache Methoden für diese Schnittstelle enthalten. Um eine Listener-Klasse zu erstellen, müssen Sie lediglich die betreffende Klasse definieren, damit sich diese über die zugehörige Schnittstelle hinaus erstreckt und dann die Methoden für jene Ereignisse überschreiben, die behandelt werden sollen. Für Maus- und Tastatur-Listener können Sie einen der drei folgenden Adapter auswählen: MouseAdapter (implementiert MouseListener), MouseMotionAdapter (implementiert MouseMotionListener) und KeyAdapter (implementiert KeyListener).
Um eine Listener-Klasse für Mausereignisse zu erstellen, verwenden Sie die nachfolgende grundlegende Klassendefinition:
import java.awt.event.*;
class MyMouseListenerClass extends MouseAdapter {
public void mousePressed(MouseEvent e) {
// Das Drücken von Maustasten behandeln
}
public void mouseReleased(MouseEvent e) {
// Das Loslassen von Maustasten behandeln
}
}
Vergessen Sie nicht, daß Paket java.awt.event am Anfang der Klasse zu importieren, damit diese die Klasse MouseAdapter finden kann. Ferner sollten Sie auch alle anderen Klassen importieren, die Sie für die Implementierung des Listeners benötigen.
Dieses spezielle Beispiel überschreibt die mousePressed()- und mouseReleased()-Methoden, die in der Schnittstelle MouseListener (als Zusatz in der MouseAdapter-Klasse implementiert) definiert sind und die MouseDown- und MouseUp-Ereignisse behandeln. Beachten Sie, daß die MouseAdapter-Klasse (und die MouseListener-Schnittstelle) zwar auch Methoden für mouseEntered und mouseExit enthält, diese aber für Sie uninteressant sind und deshalb ignoriert werden können.
Anstatt separate Listener-Klassen zu erstellen, können Sie auch ein Applet entwerfen, das sozusagen sein eigener Listener ist. Es spricht nichts gegen ein solches Applet, denn in den meisten Fällen ist es einfacher und schneller, ein Applet in das Ereignismodell 1.1 zu konvertieren, welches auf dem alten Ereignismodell 1.02 aufgebaut ist.
Um ein Applet zu erstellen, das gleichzeitig Listener ist, müssen Sie das Applet so ändern, daß es eine geeignete Listener-Schnittstelle implementiert. Führen Sie dazu die folgenden Schritte aus:
1. Stellen Sie sicher, daß java.awt.event.* importiert ist.
Die Änderung der Klassendefinition ist der einfachste Teil. Im folgenden finden Sie hierfür ein Beispiel, das die Schnittstellen MouseListener und KeyListener verwendet:
public class MyApplet extends java.applet.Applet
implements MouseListener,KeyListener {
...
}
Ergänzen Sie die Namen der Listener-Schnittstellen, die für Sie interessant sind. Sie müssen keine Listener für Ereignisse einfügen, die für Ihr Applet nicht von Bedeutung sind.
Fügen Sie anschließend die Zusätze für die Methoden in diese Schnittstelle ein. Eine grundlegende Regel zu den Schnittstellen (über die Sie am 16. Tag ausführliche Informationen erhalten) besagt, daß nach der Entscheidung für die Implementierung einer Schnittstelle, Methodendefinitionen für alle Methoden in dieser Schnittstelle erstellt werden müssen. Im Falle der Schnittstelle Runnable ist diese Aufgabe nicht schwer. Diese Schnittstelle verfügt nur über eine Methode: run(). Für die Implementierung der Ereignis-Listener müssen Sie eventuell zwei, drei oder mehr Methoden erstellen.
Dies ist kein Grund zur Aufregung. Um die Regel zu erfüllen, müssen Sie lediglich jeweils die Methodenzusätze für jede der Methoden in die Schnittstelle einfügen (dies ist genau jene Aktion, welche von den Listener-Adaptern ausgeführt wird). Sie müssen keinen Code einfügen. Der Code muß erst dann in den Methoden definiert werden, wenn Sie diesen wirklich benötigen.
Im folgenden finden Sie ein Beispiel hierfür. Angenommen, Sie haben die Schnittstelle MouseListener implementiert. Sie haben die Unterschriften für die Schnittstelle MouseListener in der API-Dokumentation herausgesucht oder diese aus einem Quellcode des JDK aus der MouseAdapter-Klasse entnommen. In beiden Fällen enthält die Schnittstelle MouseListener fünf Methoden und die Zusätze für diese Methode, die Sie in die Klasse einfügen, sehen etwa wie folgt aus:
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
Nachdem Sie die Zusätze eingefügt haben, besteht der letzte Schritt darin, den Code für die Methoden einzufügen, an denen Sie tatsächlich interessiert sind. Zum Beispiel: Wenn Sie die mouseDown-Ereignisse behandeln möchten, fügen Sie den Ereigniscode in die Methode mousePressed() ein. Sie können die anderen Methoden leer lassen.
Der letzte Schritt, nachdem Sie eine Listener-Klasse erstellt bzw. Ihr Applet als eigenen Listener definiert haben, besteht darin, die beiden miteinander zu verbinden und das System darüber zu informieren, daß Sie Ereignisse erhalten möchten. Dies geschieht, indem Sie den Listener mit jenem Objekt »registrieren«, das die Methode erhält, um auf diese Weise beide miteinander zu verbinden.
Zur Registrierung von Listenern verwenden Sie spezielle Methoden. Diese Methoden sind in der Component-Klasse definiert oder in deren Subklassen. Sie stehen allen AWT-Komponenten, einschließlich Applets, zur Verfügung. Für jeden Listenertyp ist eine Listener-Registriermethode vorgesehen. Diese Methoden lernen Sie genauer kennen, wenn die verschiedenen Typen von Ereignissen erläutert werden. Für die drei hier erwähnten Listener, lauten die Methoden wie folgt:
Alle drei Methoden verwenden ein einziges Argument: ein Objekt, das die betreffende Schnittstelle implementiert. Wenn Sie also Ihre Listener in verschiedenen Klassen implementieren, erstellen Sie eine Instanz dieser Listener-Klasse und leiten diese dann an die richtige Methode weiter. Siehe folgendes Beispiel:
ml = new MyMouseListenerClass();
addMouseListener(ml);
Wenn Sie das Applet als eigenen Listener abgewandelt haben, verwenden Sie this als Argument in der Listener-Registriermethode.
addMouseListener(this);
Die Listener-Registrierung findet im allgemeinen in der init()-Methode eines Applet statt, weil es sich um eine Aufgabe handelt, die ausgeführt werden soll, sobald das Programm startet. Nachdem ein Listener registriert wurde, beginnt das Applet damit, Ereignisse zu empfangen und der Listener kann diese verarbeiten.
Applets oder Anwendungen zu konvertieren, die das ältere Modell verwenden, ist kein großes Problem. In vielen Fällen genügt es, einige neue Codezeilen einzufügen, und den Code durch Kopieren und Einfügen an die richtige Stelle zu setzen, damit er funktioniert. In folgenden finden Sie eine Übersicht zu jenen Punkten, die durchgeführt werden müssen, damit die neuen Ereignisse so schnell wie möglich zu behandeln (im nächsten Abschnitt arbeiten Sie ein Beispiel durch):
Sie haben in dieser Lektion bereits ein Applet namens Lines erstellt, das 10 Linien am Bildschirm zeichnet. Dieses Applet soll nun aus dem alten Ereignismodell 1.02 in das neue Ereignismodell 1.1 konvertiert werden.
Der erste Schritt besteht darin herauszufinden, welche Listener-Schnittstelle Sie verwenden müssen. Ein schneller Blick zurück auf Listing 11.2 zeigt uns, daß das alte Lines-Applet mouseDown-, mouseUp- und mouseDrag-Ereignisse verwendet. Die ersten beiden befinden sich in der Schnittstelle MouseListener, während letzteres im MouseMotionListener liegt. Sie müssen also diese beiden Schnittstellen implementieren, um alle drei Ereignisse abzudecken.
Beginnen Sie mit der grundlegenden Klassendefinition, die diese Schnittstellen beinhaltet. Beachten Sie, daß hier auch die Zeile import für das Paket java.awt.event hinzugefügt wurde:
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.*;
public class LinesNew extends java.applet.Applet
implements MouseListener,MouseMotionListener {
...
}
Im nächsten Schritt fügen Sie die Zusätze für die einzelnen Methoden ein, die jeweils vom MouseListener und MouseMotionListener definiert werden. Insgesamt verfügen diese über sechs Methoden. Im folgenden sind deren Definitionen von MouseAdapter und MouseMotionAdapter kopiert und eingefügt worden:
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
)
public void mousePressed(MouseEvent e) {
)
public void mouseReleased(MouseEvent e) {
)
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
Sie haben nun die Anforderungen für die Implementierung von Schnittstellen erfüllt und können den Ereigniscode einfügen, der das Applet zu einer Aktion veranlaßt. Sie verfügen bereits über die alten Methoden mouseDown(), mouseUp() und mouseDrag() von der alten Version dieses Applet. Diese alten Methoden entsprechen den Methoden mousePressed(), mouseReleeased() und mouseDragged() des neuen Modells. Um diese Methoden zu konvertieren, kopieren Sie einfach den Code der alten Methoden und fügen diesen in die neuen ein. Löschen Sie anschließend die alten Methoden. Sie können auch alle Return-Anweisungen entfernen; die neuen Methoden verwenden keine Return-Werte, die deshalb nicht erforderlich sind. Die neuen Methoden sehen wie folgt aus:
public void mousePressed(MouseEvent e) {
if (currline < MAXLINES)
anchor = new Point(x,y);
else
System.out.println("Too many lines.");
}
public void mouseReleased(MouseEvent e) {
if (currline < MAXLINES)
addline(x,y);
}
public void mouseDragged(MouseEvent e) {
if (currline < MAXLINES) {
currentpoint = new Point(x,y);
repaint();
}
}
Das Kopieren und Einfügen des alten Ereigniscodes in die neuen Ereignismethoden kann ohne weitere Änderungen funktionieren, aber meist müssen Sie zusätzlich folgendes beachten (eigentlich zwei Dinge). Das alte Ereignismodell verwendete drei Argumente: das Ereignis-Objekt und die X- und Y-Koordinaten (in diesem Fall also sogar drei). Diese müssen geändert werden.
Das Ereignis-Objekt, das in diesem Fall weitergeleitet wird (eine Instanz von MouseEvent) enthält glücklicherweise Methoden, mit denen sich die X- und Y-Werte beibehalten lassen: getX() und getY(). Wann immer Sie also einen Bezug zu X finden, sollten Sie diesen mit e.getX() ersetzen und den Y-Wert entsprechend durch e.getY(). Siehe folgendes Beispiel:
anchor = new Point(e.getX(),e.getY());
Für die Konvertierung des Applet sind nun nur noch zwei Schritte durchzuführen. Der erste besteht darin, den Rest des Codes nicht zu vergessen, der dieses Applet bildet: die Instanzvariablen und die Methoden für addline() und paint(). Ich möchte diese hier nicht wiederholen, weil sie mit dem alten Code identisch sind.
Der letzte Schritt besteht darin, den Listener zu registrieren, damit das Applet die Ereignisse empfangen kann und die Ereignismethoden zur richtigen Zeit aufgerufen werden. Sie plazieren die Listener-Verwaltung innerhalb der init()-Methode für das Applet und verwenden dazu die folgenden beiden Methoden: addMouseListener() und addMouseMotionListener(). Da Sie zwei Listenertypen verwenden, müssen Sie beide Listener-Registrierungen durchführen. Jeder Listener muß einzeln registriert werden.
Die Argumente für die Listener-Registrierungsmethoden sind die Listener selbst. In diesem Fall haben Sie das Applet in einen Listener konvertiert, weshalb Sie für beide Methoden das Argument this verwenden können:
public void init() {
setBackground(Color.white);
addMouseListener(this);
addMouseMotionListener(this);
}
Sobald die Listener registriert sind, ist die Aufgabe gelöst! Sie können das Applet unter 1.1 JDK kompilieren und es wird im neuen Ereignismodell 1.1 ebenso funktionieren. Beachten Sie, daß das Ereignismodell 1.1 nicht in der Umgebung von 1.02 verwendet werden kann. Wenn Sie diese Applet-Variante testen möchten, müssen Sie dies mit einem Browser oder Viewer erledigen, der 1.1 unterstützt.
Listing 11.4 zeigt den komplette Quellcode für das Applet LinesNew, die konvertierte Version des ursprünglichen Lines-Applet.
Listing 11.4: Das LinesNew Applet
1: import java.awt.Graphics;
2: import java.awt.Color;
3: import java.awt.Point;
4: import java.awt.event.*;
5:
6: public class LinesNew extends java.applet.Applet
7: implements MouseListener,MouseMotionListener {
8:
9: final int MAXLINES = 10;
10: Point starts[] = new Point[MAXLINES]; // Startpunkte
11: Point ends[] = new Point[MAXLINES]; // Endpunkte
12: Point anchor; // Start der aktuellen linie
13: Point currentpoint; // Ende der aktuellen Linie
14: int currline = 0; // Anzahl der Linien
15:
16: public void init() {
17: setBackground(Color.white);
18: // Event-Listeners registrieren
19: addMouseListener(this);
20: addMouseMotionListener(this);
21: }
22:
23: // Die vorgaben der Listener-Schnittstellen erfüllen
24: public void mouseMoved(MouseEvent e) {}
25: public void mouseClicked(MouseEvent e) {}
26: public void mouseEntered(MouseEvent e) {}
27: public void mouseExited(MouseEvent e) {}
28:
29: // wie mouseDown
30: public void mousePressed(MouseEvent e) {
31: if (currline < MAXLINES)
32: anchor = new Point(e.getX(),e.getY());
33: else
34: System.out.println("Too many lines.");
35: }
36:
37: // wie mouseUp
38: public void mouseReleased(MouseEvent e) {
39: if (currline < MAXLINES)
40: addline(e.getX(),e.getY());
41: }
42:
43: // wie mouseDrag
44: public void mouseDragged(MouseEvent e) {
45: if (currline < MAXLINES) {
46: currentpoint = new Point(e.getX(),e.getY());
47: repaint();
48: }
49: }
50:
51: void addline(int x,int y) {
52: starts[currline] = anchor;
53: ends[currline] = new Point(x,y);
54: currline++;
55: currentpoint = null;
56: anchor = null;
57: repaint();
58: }
59:
60: public void paint(Graphics g) {
61: // Bestehende Linien zeichnen
62: for (int i = 0; i < currline; i++) {
63: g.drawLine(starts[i].x, starts[i].y,
64: ends[i].x, ends[i].y);
65: }
66: // aktuelle Linie zeichnen
67: g.setColor(Color.blue);
68: if (currentpoint != null)
69: g.drawLine(anchor.x,anchor.y,
70: currentpoint.x,currentpoint.y);
71: }
72:}
In den vorhergehenden Abschnitten, haben Sie die Grundlagen des neuen Ereignismodells 1.1 kennengelernt und wissen nun, wie sich ein einfaches Applet konvertieren läßt, das zu Beginn dieser Lektion erstellt wurde. Im folgenden Abschnitt erhalten Sie einen kurzen Überblick über die Änderungen zwischen dem Modell 1.02 und 1.1 in den Maus- und Tastaturereignissen selbst und bei deren Behandlung.
Im Ereignismodell 1.02 waren alle Maus- und Tastaturereignisse Instanzen der Event-Klasse. In 1.1 werden diese in MouseEvent-, MouseMotionEvent- und KeyEvent-Klassen unterteilt. Alle diese drei Klassen erben von InputEvent und sind im Paket java.awt.event enthalten. Diese Unterteilung der Ereignisse in verschiedene Klassen ändert sich, je nachdem wie Sie spezielle Maus- und Tastaturereignisse in Ihren eigenen Klassen behandeln.
Mausereignisse werden durch zwei Klassen definiert: MouseEvent und MouseMotion-Event. Die Listener-Schnittstellen für diese Ereignisse sind in MouseListener und MouseMotionListener definiert. Sie können also folgende Unterschiede zwischen dem Modell 1.02 und 1.1 bei Mausereignissen feststellen:
Beachten Sie, daß die 1.1-Mausmethoden keine X- und Y-Argumente verwenden (wie häufig bei den alten 1.02-Methoden der Fall). Um sich auf X- und Y-Positionen für einen Mausklick oder ein Mausziehen zu beziehen, müssen Sie die Methoden getX() und getY() verwenden, die vom Ereignis-Objekt selbst definiert werden. Im Applet Spots finden Sie hierfür ein Beispiel.
Um zweifache und dreifache Mausklicks zu behandeln, verwenden Sie die getClickCount()-Methode in MouseEvent anstelle der Instanzvariablen clickCount, aus dem Modell 1.02. Das Ergebnis bleibt dasselbe.
Die Behandlung von Klicks zusammen mit Ergänzungstasten oder Klicks auf verschiedenen Maustasten wird in derselben Weise durchgeführt, wie im Modell 1.02 (diese Technik wurde bereits in einem vorherigen Abschnitt beschrieben): mittlere und linke Maustaste werden mit den Tasten Steuerung und Meta aufgezeichnet. Die metaDown()- und controlDown()-Methoden von 1.02 erhalten im neuen Modell die Namen isMetaDown() und isControlDown() und werden in InputListeners definiert (damit stehen sie sowohl für Maus- als auch für Tastatur-Listener zur Verfügung).
Tasten-Ereignisse werden nun durch die KeyEvent-Klasse definiert, eine Subklasse von InputEvent, und sollten in Ihren Programmen mit KeyListener behandelt werden. Zwischen den Modellen 1.02 und 1.1 gibt es folgende Unterschiede:
Sie können auf Modifizierungstasten (Umsch, Strg, Alt, Meta) testen, indem Sie die in InputEvent definierten Methoden verwenden (diese stehen sowohl für die Maus als auch für die Tastatur zur Verfügung): isShiftDown(), isControlDown(), isAltDown() und isMetaDown(). Alle vier Methoden geben die booleschen Werte true oder false zurück.
Um spezielle Tasten wie die Funktionstasten, Bild auf, Bild ab usw. zu behandeln, können Sie virtuelle Tasten verwenden. Bei diesen virtuellen Tasten handelt es sich um Klassenvariablen, die von der KeyEvent-Klasse definiert werden. Diese Tasten stellen jede beliebige Taste auf jeder nur denkbaren Tastatur dar (sie sind ähnlich den Klassenvariablen für spezielle Tasten, die in der Event-Klasse von 1.02 definiert sind, dienen aber allgemeineren Zwecken). Sie können diese zunächst in Ihrem Code mit der getKeyCode()-Methode (von KeyEvent definiert) und dann erneut auf einen speziellen Wert testen lassen. Siehe folgendes Beispiel:
if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
// Eine Bild ab-Taste verarbeiten
}
Da verschiedene Tastaturen verschiedene numerische Werte für verschiedene Tasten erzeugen können, ist der Test auf die virtuellen Tasten ein besser Ansatz als der Test auf hart codierte Werte. Jede Java-Implementation auf einer speziellen Plattform erzeugt dann die korrekten virtuellen Tasten für die einzelnen Tasten. Beachten Sie jedoch, daß die Tastaturen verschiedener Plattformen nicht über dieselben Tasten verfügen und das Java bei der Aufzeichnung für die verschiedenen Plattformen keine Unterschiede macht.
Die komplette Liste aller virtuellen Tasten finden Sie in der API-Dokumentation für die KeyEvent-Klasse. Tabelle 11.3 zeigt einen Auszug aus dieser Liste.
Tabelle 11.3: Ein Teil der Liste der virtuellen Tasten.
Klassenvariable | |
VK_CLEAR |
Löschtaste |
VK_DOWN |
Pfeil unten |
VK_END |
Ende-Taste |
VK_ESCAPE |
Escape (Esc) |
VK_F1 bis VK_F12 |
Funktionstasten |
VK_HELP |
Hilfetaste |
VK_HOME |
Pos1-Taste |
VK_INSERT |
Einfüge-Taste (Einfg) |
VK_LEFT |
Pfeil links |
VK_NUMPAD0 bis VK_NUMPAD9 |
Zahlen auf dem Ziffernblock |
VK_PAGE_DOWN |
Bild-unten-Taste |
VK_PAGE_UP |
Bild-auf-Taste |
VK_PAUSE |
Pause-Taste |
VK_PRINTSCREEN |
Druck-Taste |
VK_RIGHT |
Pfeil rechts |
VK_UP |
Pfeil links |
Ereignisse stellen die Art dar, in welcher Ihr Programm auf Aktionen reagiert, die stattfinden, während das Programm ausgeführt wird. Dies ist meistens der Fall, wenn der Benutzer die Maus oder Tastatur dazu verwendet mit einzelnen Elementen des Applet oder der Anwendung in Interaktion zu treten. In dieser Lektion wurden die grundlegenden Merkmale von Ereignissen und deren Funktionsweise erläutert, und Sie haben erfahren, wie Sie sowohl im älteren Ereignismodell 1.02 als auch im neueren Ereignismodell 1.1 des AWT-Ereignissystems mit Ereignissen verfahren können.
Die Behandlung von Ereignissen im 1.02 Abstract Windowing Toolkit von Java ist einfach. Im wesentlichen müssen Sie hier nur die richtige Methode im Applet-Code verwenden und das Applet empfängt und behandelt das Ereignis zum richtigen Zeitpunkt. Die Methoden, die Sie in dieser Lektion kennengelernt haben umfaßten mouseUp() und mouseDown() für Mausklicks, mouseMove() und MouseDrag() für Mausbewegungen, mouseEnter() und mouseExit() für das »Betreten« und Verlassen des Applet-Bereichs mit dem Mauszeiger und keyDown() und keyUp() für das Drükken von Tasten auf der Tastatur. Ferner haben Sie die handleEvent()-Methode, die übergeordnete Methode zu den einzelnen Ereignis-Methoden, und die Mechanismen zur Aufnahme von Ereignissen kennengelernt, die keine einzelnen Methoden überschreiben.
Bei Java 1.1 hat sich das Ereignismodell geändert. Wie Ereignisse behandelt und im Code verwaltet werden, ist äußerst unterschiedlich. Im Ereignismodell 1.1 empfängt das Applet nur jenes Ereignis, wenn das Applet einen Listener dafür registriert hat. Listener wiederum sind Klassen, die eine Listener-Schnittstelle für einen Satz von Ereignissen implementieren und diese Ereignisse bei deren Eingang behandeln. Sie können separate Listener-Klassen erstellen oder ein Applet so abändern, daß dieses selbst als Listener für die eigenen Ereignisse fungiert. Im letzten Teil dieser Lektion haben Sie die Listener und Ereignisse aus dem neuen Modell 1.1 kennengelernt und einen Code aus dem alten Modell in das neue konvertiert.
F Im Applet Spots, wurden die Punkt-Koordinaten in Arrays gespeichert, die eine beschränkte Größe haben. Wie kann ich dieses Applet so verändern, daß eine unbegrenzte Anzahl von Punkten gezeichnet wird?
A Dazu stehen einige Möglichkeiten zur Verfügung.
Als erstes können Sie in der addspot()-Methode testen, ob die Anzahl der Punkte MAXSPOTS überschreitet. Dann erstellen Sie ein größeres Array (mit der System.arraycopy()-Methode) und weisen die X- und Y-Arrays wieder diesem neuen, größeren Array zu.
Als zweites können Sie die Vector-Klasse verwenden. Vector, ein Teil des Pakets java.util, implementiert ein Array, das automatisch zunimmt eine Art verknüpfte Liste aus anderen Sprachen. Der Nachteil von Vector besteht darin, daß alles, was in Vector eingefügt werden kann, ein aktuelles Objekt sein muß. Sie müssen also Ganzzahlen zu Integer-Objekten umwandeln und dann deren Werte aus den Integer-Objekten extrahieren, um diese erneut als Ganzzahlen behandeln zu können. Die Vector-Klasse ermöglicht den Zugang und die Veränderung von Elementen in Vector auf dieselbe Weise wie in einem Array (indem Methodenaufrufe anstelle der Arraysyntax erfolgen). Experimentieren Sie mit diesen Möglichkeiten.
F Was ist eine Meta-Taste?
A Diese gibt es häufig auf UNIX-Systemen und wird auf den meisten Tastaturen mit Alt gekennzeichnet (auf Mac als Command). Da die Umschalt- und Steuerungstaste weiter verbreitet sind, sollten Sie Ihre Oberfläche nach Möglichkeit auf der Basis dieser Modifizierungstasten einrichten.
F Ich habe im API die Event-Klasse nachgeschlagen (im 1.02-Modell) und hier sind viel mehr Ereignistypen aufgelistet als von Ihnen heute erwähnt wurden.
A Ja. Die Event-Klasse definiert viele verschiedene Arten von Ereignissen, beide für allgemeine Benutzereingaben, wie z.B. die Maus- und Tastaturereignisse, die Sie heute kennengelernt haben. Darüber hinaus gibt es aber auch Ereignisse für Änderungen im Status von Oberflächenkomponenten, z.B. der Fenster und Bildlaufleisten. Morgen erfahren Sie mehr über diese anderen Ereignisse.
F Adapter-Klassen, so wie ich das verstehe, implementieren die verschiedenen Methoden als Zusätze in die Ereignis-Listener. Diese Klassen lassen sich dann ausweiten, um neue Listener zu erstellen. Wenn die Adapter-Klassen so sinnvoll sind, warum kann das Applet dann nicht von ihnen erben?
A Ihr Applet verfügt bereits über eine Superklasse: java.applet.Applet. Und in Java können Sie anders als in anderen Programmiersprachen nicht mehrere Superklassen verwenden. Wenn Sie also keine separaten Listener-Klassen erstellen, sind die Adapter-Klassen nutzlos. Sie müßte statt dessen direkt die Schnittstellen verwenden.
F Warum muß man in der Applet-Klasse Zusatzmethoden erstellen, um die Listener-Oberfläche zu implementieren? Das scheint doch eher unsinnig. Warum kann man die benötigten Methoden nicht einfach überschreiben?
A Weil hier keine Überschreibung stattfindet, sondern deren ursprüngliche Implementierung erfolgt. Am 16. Tag erhalten Sie weitere Informationen zu den Schnittstellen, aber es ist wichtig zu wissen, daß die Schnittstellen einen Satz mit Methodendeklarationen enthalten. Wenn Sie die Schnittstellen-Schlüsselwörter zu implements in die Klassendefinition einfügen, versprechen Sie damit, alle Methoden in dieser Schnittstelle zu unterstützen und nicht nur jene, die Sie benötigen. Sie müssen also sicherstellen, daß alle diese Methoden definiert sind.
Wie bereits erwähnt, reicht es jedoch für die Erfüllung dieser Regel aus, daß die Methoden vorhanden sind. Diese müssen keine Aktionen ausführen. Dies erledigt der Methodenzusatz.
F Was ist der Unterschied zwischen einem Listener, der als separate Klasse erstellt wird und einem Applet, das ebenfalls als Listener operiert? Welche Vor- oder Nachteile sind mit diesen beiden Techniken verbunden?
A Wenn Sie einen Listener als separate Klasse erstellen, können Sie die Adapter-Klasse als Superklasse verwenden und dann nur jene Methoden überschreiben, die Sie benötigen. Dies bedeutet, daß die Klassen mit weniger Methodenzusätzen beladen sind. Separate Listener-Klassen bieten auch den Vorteil, das sie sich getrennt voneinander kompilieren lassen, wenn Sie also die Reaktion eines Applet/Objekts auf ein Ereignis ändern möchten, können Sie diese Änderung einfach im Listener vornehmen und diesen dann neu kompilieren. Sie müssen also nicht das gesamte Programm neu kompilieren. Und schließlich bieten die separaten Listener-Klassen noch einen Vorteil: Wenn ein Listener zu umfangreich wird, können Sie das Verhalten in mehrere Listener-Klassen aufteilen, alle registrieren und diese dann getrennt voneinander verwalten.
Wenn Sie ein Applet als Listener verwenden, ist der Vorgang zunächst zwar einfacher, d.h. Sie müssen weniger Code eingeben und die Konvertierung von alten Applet-Code läßt sich ebenfalls einfacher durchführen. Es ist sicher sinnvoll, zu Beginn das Applet selbst als Listener zu verwenden, um sich daran zu gewöhnen und dann auf die separaten Listener-Klassen umzusteigen.
F Ich habe ein Applet mit beiden Techniken darin: Tasten-Listener und mouseDown()-Methoden. Die Tastaturereignisse funktionieren problemlos, aber die Mausereignisse werden ignoriert. Was ist da los?
A Wie bereits in dieser Lektion erwähnt, können Sie entweder das Ereignismodell 1.02 oder das Ereignismodell 1.1 verwenden, aber nicht beide miteinander kombinieren. Sie müssen die alten mouseDown()-Methoden in Listener-Methoden für die Maus konvertieren, damit diese ausgeführt werden können.
(c) 1997 SAMS