vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 2

Tag 13


Ereignisverarbeitung in Applets

Mit den Fähigkeiten, die Sie bisher entwickelt haben, können Sie eine grafische Benutzerschnittstelle sehr schön gestalten, allerdings fehlt noch das Gehirn. Sie sieht zwar wie eine funktionierende Benutzerschnittstelle aus - man kann Schaltflächen und andere Elemente der Benutzerschnittstelle bedienen -, allerdings führt dies zu keinerlei Reaktion.

Um eine Benutzerschnittstelle in Java mit Funktionalität zu versehen, müssen Sie lernen, wie Sie ein Programm dazu bringen, auf Ereignisse zu reagieren. Ereignisse sind Methodenaufrufe, die die Fensterverwaltung von Java erzeugt, wenn ein Element der Benutzerschnittstelle verwendet wird. Die große Bandbreite der Ereignisse deckt die Verwendung von Maus und Tastatur ab, inklusive Mausklicks, Mausbewegungen und Tastenanschlägen.

Heute lernen Sie, wie Sie ein Applet dazu bringen, Ereignisse mit den Techniken von Java 1.02 zu verarbeiten, so daß Ihre Programme in jedem Web-Browser ausgeführt werden können, der Java unterstützt. An Tag 21 lernen Sie Ereignisse mit den Techniken von Java 1.2 zu verarbeiten.

Ereignisverarbeitung

Eines der Dinge, die Sie bei der Erstellung Ihres ersten Applets gelernt haben, ist, daß bei der Ausführung des Programms vieles hinter den Kulissen geschieht. Das Fensterverwaltungssystem von Java ruft Methoden, wie z.B. paint(), init() und start(), automatisch auf, wenn diese benötigt werden, ohne daß Sie sich darum kümmern müssen.

Wie die Applet-Programmierung schließt die Ereignisbehandlung Methoden ein, die automatisch aufgerufen werden, wenn eine Aktion eines Benutzers ein Ereignis auslöst.

Ereignisarten

Ein Ereignis wird als Reaktion auf nahezu alles, was ein Benutzer während des Lebenszyklus eines Java-Programms tun kann, erzeugt. Jede Bewegung der Maus, ein Klick auf eine Schaltfläche oder ein Tastenanschlag erzeugen ein Ereignis.

In Ihren Programmen müssen Sie sich nicht um alle Ereignisse kümmern, die auftreten könnten. Statt dessen verarbeiten Sie nur die Ereignisse, auf die Ihr Programm reagieren soll. Wenn z.B. der Benutzer mit der Maus innerhalb des Applet-Fensters klickt oder eine Taste drückt, dann wollen Sie eventuell, daß Ihr Programm eine bestimmte Aktion als Reaktion auf das Ereignis ausführt.

Die folgenden Ereignisse sind einige der Ereignisse, die Sie in Ihren eigenen Programmen behandeln können. (In der Auflistung finden Sie die englische Bezeichnung für die einzelnen Ereignisse, wie sie unter Java üblich sind. In Klammern dahinter befindet sich jeweils eine Beschreibung der Aktion, die das Ereignis auslöst):

Die Methode handleEvent()

Die Ereignisbehandlung ist der Bereich, in dem sich Java zwischen Java 1.02 und der aktuellen Version Java 1.2 verändert hat. Die Ereignisse selbst werden nahezu gleich erzeugt und durchlaufen das System auch in nahezu der gleichen Weise, unabhängig davon, mit welcher Version der Sprache Sie ein Programm schreiben. Der Unterschied liegt darin, wie Ereignisse empfangen und verarbeitet werden.

In Java 1.02 durchlaufen alle Ereignisse, die während des Lebenszyklus Ihres Java- Programms erzeugt werden, dieses Programm und werden von einer Methode names handleEvent() behandelt. Diese Methode ist in der Klasse Component definiert, von der die Klasse java.applet.Applet abgeleitet ist. Dadurch steht diese Methode in allen Ihren Applets zur Verfügung.

Wenn ein Ereignis an die Methode handleEvent() in Ihrem Applet geschickt wird, ruft diese eine speziellere Methode zur Verarbeitung des jeweiligen Ereignisses auf. Einige dieser spezielleren Methoden sind mouseDown(), mouseUp() und keyDown().

Um ein Ereignis in Ihren Applets verarbeiten zu können, überschreiben Sie eine dieser spezielleren Methoden zur Ereignisbehandlung. Anschließend wird, sobald dieses Ereignis auftritt, Ihre Methode aufgerufen. Sie könnten z.B. die Methode mouseDown() mit dem Verhalten, das für die Anzeige einer Meldung im Applet-Fenster sorgt, überschreiben. Wenn nun ein mouseDown-Ereignis auftritt, wird diese Nachricht angezeigt.

Mausklicks behandeln

Zu den gängigsten Ereignissen in einem Applet zählen die Mausklicks. Mausklickereignisse finden statt, wenn ein Benutzer eine beliebige Position innerhalb des Applets 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 Interaktionen durchgeführt werden sollen.

mouseDown und mouseUp

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ükken, 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 Benutzerinteraktion 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 Methodensignatur für ein mouseDown-Ereignis:

public boolean mouseDown(Event evt, int x, int y) {
...
}

Die mouseDown()-Methode (und auch die mouseUp()-Methode) erhält 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 Event-Objekt 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 auf die x- und y-Koordinaten innerhalb des Event-Objekts direkt zugreifen können; sie werden als separate Variablen weitergeleitet, wodurch sie einfacher zu behandeln sind.

Im folgenden finden Sie eine einfache Methode, welche 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.


Anders als bei Java-Anwendungen, wo Ausgaben von System.out.print() am Bildschirm erfolgen, variiert die Ausgabe in Applets von System zu System und von Browser zu Browser. Der Appletviewer zeigt die Zeile im selben Fenster an, in dem auch die Anweisung gegeben wurde. Netscape verfügt über ein spezielles Fenster namens Java-Konsole, das sichtbar sein muß, damit diese Ausgabe erscheint. Der Internet Explorer leitet Java-Ausgaben in eine separate Datei. Testen Sie in den jeweiligen Umgebungen, wo die Ausgabe von Java-Applets erfolgt.

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 die übergeordnete Komponente im gesamten System abzugeben ist. Die allgemeine Regel lautet, daß Methoden, die Ereignisse behandeln, 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 verarbeiten. In den meisten Beispielen der heutigen Lektion werden Sie auf einfache Ereignisse reagieren, weshalb hier immer true zurückgegeben wird.

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) {
....
}

Beispiel: Punkte

In diesem Abschnitt erstellen Sie ein Beispiel-Applet, das das Ereignismodell 1.02 zur Behandlung von Mausereignissen - insbesondere mouseDown-Ereignissen - verwendet. Das Spots-Applet beginnt mit einem leeren Bildschirm und wartet dann ab. Wird die Maus in diesem Bildschirm geklickt, erscheint ein blauer Punkt. Sie können in diesem Bildschirm bis zu zehn Punkte anordnen. Abbildung 13.1 zeigt das Spots-Applet.


Abbildung 13.1:
Das Spots-Applet

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 Punkts zu verfolgen.


In der Definition der Event-Klasse ist implements Runnable nicht enthalten. Wie Sie im weiteren Verlauf der Arbeit mit diesem Applet noch sehen werden, verfügt es auch nicht über eine run()-Methode. Warum nicht? Weil das Applet im eigentlichen Sinn selbst keine Aufgaben durchführt, es wartet im Grunde nur auf Benutzereingaben und reagiert darauf. Es besteht keine Notwendigkeit für Threads, wenn ein Applet nicht die ganze Zeit über selbst aktiv ist.

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 Applets 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 Punkts 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 Punkt 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() gehen Sie die in den xspots- und yspots-Arrays gespeicherten Punkte in einer Schleife durch, damit einer nach dem anderen gezeichnet wird (leicht nach rechts oben gerückt, damit der Punkt rund um den Mauszeiger und nicht unterhalb rechts ausgegeben wird).

Das war's 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 13.1.

Listing 13.1: Der gesamte Quelltext von Spots.java

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: }

Sie können dieses Applet in einer Webseite mit dem folgenden HTML-Code laden:

<applet code="Spots.class" height=250 width=250>
</applet>

Doppelklicks

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
      ....
   }
}

Einen wichtigen Punkt gibt es zu beachten, wenn Sie auf Doppel- oder Tripel-Klicks prüfen: mouseDown() wird für jeden Klick mit der Maus aufgerufen. Nehmen Sie das folgende Code-Beispiel:

public boolean mouseDown(Event evt, int x, int y) {
    system.out.println("Click count: " + evt.clickCount);
    return false;
}

Wenn Sie diese Methode in ein Applet einfügen, wird folgendes angezeigt, wenn mit der Maus dreimal geklickt wird:

Click count: 1
Click count: 2
Click count: 3

Wie Sie etwas später am heutigen Tag lernen werden, erzeugen einige Komponenten ein action-Ereignis, wenn doppelt auf diese Komponenten geklickt wird. Aus diesem Grund ist es nicht immer nötig, die mouseDown()-Methode zu verwenden, um zwischen einfachen und doppelten Klicks, die von einer Komponente erzeugt wurden, zu unterscheiden.

Mausbewegungen behandeln

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 Applets, einer Komponente oder eines Containers auf dem Applet bzw. bei dessen Verlassen erzeugt.

Für jedes dieser Ereignisse werden spezielle Methoden definiert, die diese Ereignisse aufgreifen, ebenso wie die Methoden mouseDown() und mouseUp() für die Verarbeitung von Mausklicks.

mouseDrag und mouseMove

Um Mausbewegungsereignisse 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 jedes Pixel, um das 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.

mouseEnter und mouseExit

Die mouseEnter()- und mouseExit()-Methoden werden aufgerufen, wenn der Mauszeiger den Applet-Bereich oder einen Bestandteil davon »betritt« bzw. verläßt. Sowohl mouseEnter() als auch mouseExit() haben ähnliche Signaturen wie die Mausklick-Methoden. Sie verfügen über drei Argumente: das Event-Objekt und die x- und y-Koordinaten des Punkts, an dem der Mauszeiger über dem Applet-Bereich steht oder an dem er den Applet-Bereich verläßt. Die folgenden Beispiele zeigen die Signaturen für mouseEnter() und mouseExit():

public boolean mouseEnter(Event evt, int x, int y) {
   ...
}

public boolean mouseExit(Event evt, int x, int y) {
   ...
}

Beispiel: Linien zeichnen

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 Abbildung 13.2 dargestellt.


Abbildung 13.2:
Linien zeichnen

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 Applets weiß sein soll (wie auch in Spots).

Die drei wichtigen Ereignisse dieses Applets 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. Das 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 Linie zeichnen müssen.

Die Ausgabe des Applets 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 verschieden von 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 13.2 zeigt den kompletten Quelltext des Lines-Applets, in dem die einzelnen Teile zusammengefaßt werden.

Listing 13.2: Der komplette Quelltext von Lines.java

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:}

Sie können dieses Applet testen, indem Sie den folgenden HTML-Code verwenden:

<applet code="Lines.class" height=250 width=250>
</applet>

Behandlung von Tastaturereignissen

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 ermitteln, die der Benutzer zur Durchführung einer Aktion gedrückt hat, oder von den Benutzern Ihres Applets eine Zeicheneingabe erhalten.

Damit ein Tastaturereignis von einer Komponente empfangen werden kann, muß diese Komponente den Eingabefokus besitzen. Anders ausgedrückt muß diese Komponente diejenige auf der Benutzerschnittstelle sein, die aktuell für die Benutzereingaben ausgewählt ist. Sie lernen über den Eingabefokus später am heutigen Tag mehr, wenn Sie mit Fokusereignissen arbeiten. Der Eingabefokus läßt sich am einfachsten verstehen, wenn Sie sich eine Benutzerschnittstelle vorstellen, die über diverse Texteingabefelder verfügt. Die Eingabemarke blinkt in dem Textfeld, das den Fokus hat, und ein Benutzer kann über die Tastatur Text in dieses Textfeld eingeben. Kein anderes Textfeld kann Text empfangen, bis es den Eingabefokus zugeteilt bekommt. Alle Komponenten, inklusive der Container, können den Fokus erhalten.

Um ausdrücklich festzulegen, daß eine Komponente den Fokus hat, kann die Methode requestFocus() der Komponente ohne Argumente aufgerufen werden. Die folgende Anweisung erteilt einer Schaltfläche (einem Button-Objekt mit dem Namen quit):

quit.requestFocus();

Sie können einem Applet-Fenster den Fokus erteilen, indem Sie dessen requestFocus() -Methode aufrufen.

keyDown und keyUp

Um ein Tastaturereignis zu behandeln, 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 behandeln, verwenden Sie die folgende keyUp()-Methode:

public booklean keyUp(Event evt, int key)  {
...
}

Standardtasten

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 (½)-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.


Falls Sie Solaris 7 einsetzen und eine Komponente nicht auf die Pfeiltasten reagiert, sondern statt dessen die Eingabefunktionen scheinbar einfrieren, so sollten sie folgende Zeilen in der Datei $HOME/.dt/$LANG/dtwmrc auskommentieren:

<Key>Down root f.circle_down
<Key>Up root f.circle_up
<Key>Right root f.next_workspace
<Key>Left root f.prev_workspace

In Tabelle 13.1 finden Sie Standardvariablen der Event-Klasse für verschiedene Tasten.

Tabelle 13.1: Standardtasten, die in der Event-Klasse definiert sind.

Klassenvariable

Taste

Event.HOME

(Pos_1)

Event.END

(Ende)

Event.PGUP

(Bild½)

Event.PGDN

(Bild¼)

Event.UP

(½)

Event.DOWN

(¼)

Event.LEFT

(æ)

Event.RIGHT

(Æ)

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)

 

Beispiel: Zeichen eingeben, anzeigen und versetzen

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. Abbildung 13.3 zeigt ein Beispiel hierfür.


Abbildung 13.3:
Das Keys-Applet

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));
    requestFocus();
}

Die letzte Anweisung in der init()-Methode erteilt dem Applet-Fenster den Eingabefokus. Diese Anweisung wird benötigt, um sicherzustellen, daß die Tastatureingaben auch von der Komponente empfangen werden, die diese behandeln sollen - hier das Applet-Fenster selbst.


In den früheren Versionen von Java war der Aufruf von requestFocus() nicht nötig, damit das Applet-Fenster Tastatureingaben empfangen konnte. Außerdem konnten Sie dem Applet-Fenster den Fokus erteilen, indem Sie in dieses geklickt haben. Dies gilt auch für die aktuellsten Versionen des Netscape Navigator und des Microsoft Internet Explorer. Der Appletviewer des JDK 1.2 macht allerdings die Verwendung von requestFocus() erforderlich. Andernfalls erhält das Applet-Fenster den Eingabefokus für Tastatureingaben nicht. Behalten Sie diesen Unterschied im Gedächtnis, wenn Sie Applets testen, die Tastaturereignisse verwenden. Über die Methode requestFocus() dem Applet-Fenster den Fokus zu erteilen, ist wahrscheinlich der beste Weg.

Da das Verhalten des Applets auf Tastatureingaben basiert, findet die Hauptarbeit des Applets 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()-Applets befindet sich eine 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 Applets kein Anfangszeichen gibt und nichts zu zeichnen ist. Das muß berücksichtigt werden. Die Variable currkey wird mit 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 13.3 zeigt den kompletten Quellcode für das Keys-Applet.

Listing 13.3: Der gesamte Quelltext von Keys.java

 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:         requestFocus();
19:     }
20:
21:     public boolean keyDown(Event evt, int key) {
22:         switch (key) {
23:         case Event.DOWN:
24:             curry += 5;
25:             break;
26:         case Event.UP:
27:             curry -= 5;
28:             break;
29:         case Event.LEFT:
30:             currx -= 5;
31:             break;
32:         case Event.RIGHT:
33:             currx += 5;
34:             break;
35:         default:
36:             currkey = (char)key;
37:         }
38:
39:         repaint();
40:         return true;
41:     }
42:
43:     public void paint(Graphics g) {
44:         if (currkey != 0) {
45:             g.drawString(String.valueOf(currkey), currx,curry);
46:         }
47:     }
48: }

Sie können das Applet mit der folgenden HTML-Anweisung testen:

<applet code="Keys.class" height=100 width=100>
</applet>

Modifizierungstasten und Maustastenkombinationen

Zu den sogenannten Modifizierungstasten zählen die Umschalttaste (ª_), 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 (ª_)-Taste 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 Modifizierungstaste aktiviert werden, um es von der üblichen Version des jeweiligen Ereignisses zu unterscheiden.


Die Taste Meta wird meist bei Unix-Systemen verwendet und entspricht der Taste (Alt) auf PC-Tastaturen und Command ((°)-Taste) auf Macintosh.

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 über das 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()
   /// Strg-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 (Ctrl)-Taste auf einem Macintosh entspricht genau dem Klicken der mittleren Maustaste auf einer Maus mit drei Tasten. Wenn Sie die (¾)-Taste (Apple) drücken und mit der Maustaste bei einem Macintosh klicken, ist diese identisch mit dem Klicken der rechten Maustaste bei einer Maus 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.

Der generische Eventhandler

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 das 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 Rumpf 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 dieser 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.

Handhabung von Ereignissen der Benutzeroberfläche

Falls Sie mit dem Bearbeiten der heutigen Lektion an dieser Stelle aufgehört haben, verfügen Sie über das Wissen, um ein Applet zu erstellen, das viele kleine Komponenten der Benutzeroberfläche enthält, mit dem entsprechenden Layout-Manager ansprechend am Bildschirm ausgelegt ist sowie Abstände und Eckeinsätze hat. Allerdings wäre dann Ihr Applet ziemlich fade, weil die Komponenten der Benutzeroberfläche eigentlich nichts tun, wenn man sie anklickt oder die entsprechende Taste drückt.

Damit Ihre Komponenten der Benutzeroberfläche etwas bewirken, ist die Verknüpfung mit einem Code zur Handhabung von Ereignissen erforderlich. Denken Sie an die Maus- und Tastaturereignisse - genauso werden durch Ereignisse der Benutzeroberfläche Reaktionen Ihrer Applets oder auf Eingaben durch den Benutzer hervorgerufen. Allerdings spielen sich die Ereignisse für AWT-Komponenten auf einer höheren Ebene ab; Schaltflächen beispielsweise verwenden Aktionsereignisse, die durch Anklicken der Schaltfläche ausgelöst werden. Sie brauchen sich über mouseDown oder mouseUp keine Gedanken zu machen; die Komponente übernimmt diese Aufgabe für Sie.

Konzentrieren wir uns heute auf die von den sechs grundlegenden Komponenten der Benutzeroberfläche erzeugten Ereignisse, mit denen Sie sich bereits beschäftigt haben. Eigentlich erzeugen lediglich fünf von ihnen Ereignisse (Labels sind statisch). Diese sechs Komponenten der Benutzeroberfläche können fünf Ereignisarten erzeugen:

Behandlung von Aktionsereignissen

Aktionsereignisse sind die bei weitem am häufigsten eingesetzten Ereignisse der Benutzeroberfläche, und daher wird für ihre Handhabung eine spezielle Methode angewandt, genau wie bei den grundlegenden Maus- und Tastaturereignismethoden, die wir gestern durchgenommen haben.

Um ein von einer beliebigen Komponente der Benutzeroberfläche erzeugtes Aktionsereignis abzufangen, definieren Sie in Ihrem Applet oder Klasse eine action()-Methode mit folgender Signatur:

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

Diese action()-Methode dürfte angesichts der bereits gelernten grundlegenden Ereignismethoden für Maus und Tastatur vertraut aussehen. Wie jene Methoden wird sie an das Event-Objekt, das die jeweilige Aktion darstellt, weitergegeben. Außerdem erhält sie ein zusätzliches Objekt (bei diesem Code den Parameter arg), das ein beliebiger Klassentyp sein kann.

Das zweite Argument für die action()-Methode hängt von der Komponente der Benutzeroberfläche ab, durch die die Aktion erzeugt wird. Die Basisdefinition ist ein durch die Komponente der Benutzeroberfläche bestimmtes »beliebiges arbiträres Argument« zur Weitergabe zusätzlicher Informationen, die Ihnen später bei der Bearbeitung der Aktion nützlich sein können. Tabelle 13.2 zeigt die zusätzlichen Argumente für jede Komponente der Benutzeroberfläche.

Tabelle 13.2: Aktionsargumente für jede Komponente der Benutzeroberfläche

Komponente

Argumenttyp

Enthält

Schaltfläche

String

Das Label der Schaltfläche

Kontrollfelder

Boolesch

Immer true

Optionsfelder

Boolesch

Immer true

Auswahlmenüs

Zeichenkette

Das Label des gewählten Elements

Textfelder

Zeichenkette

Textinhalt des Felds

Als erstes müssen Sie innerhalb der action()-Methode testen, welche Komponente der Benutzeroberfläche die Aktion erzeugt hat (im Gegensatz zu Ereignissen der Maus oder Tastatur, wo es nicht so eine große Rolle spielt, da alle unterschiedlichen Komponenten Aktionen erzeugen können). Um dies zu vereinfachen, beinhaltet das Event- Objekt, das Sie beim Aufruf von action() erhalten, eine target-Instanzvariable, die eine Referenz zu dem Objekt, das das Ereignis aufgenommen hat, enthält. Sie können den instanceof-Operator wie folgt benutzen, um herauszufinden, von welcher Komponente das Ereignis ausging:

public boolean action(Event evt, Object arg) {
   if (evt.target instanceof TextField)
    return handleText(evt.target);
  else if (evt.target instanceof Choice)
    return handleChoice(arg);
  //...

    return false;
}

In diesem Beispiel könnte action() entweder durch ein TextField oder ein Auswahlmenü erzeugt worden sein; die if-Anweisungen bestimmen, von welchem der beiden das Ereignis erzeugt wurde, und rufen zur entsprechenden Bearbeitung eine andere Methode auf (handleText() oder hier handleChoice()). (Weder handleText() noch handleChoice() sind AWT-Methoden. Es wurden einfach zwei beliebige Hilfsmethoden ausgewählt. Die Erzeugung dieser Hilfsmethoden ist allgemein üblich, um zu verhindern, daß action() mit viel Code vollgestopft wird.)

Wie bei anderen Ereignismethoden, gibt action() einen booleschen Wert aus, und Sie sollten true ausgeben, falls action() das Ereignis selbst behandelt, oder false, falls das Ereignis weitergegeben (oder ignoriert) wird. In unserem Beispiel haben Sie die Kontrolle an die Methode handleText() oder handleChoice() weitergegeben, und es ist deren Aufgabe, true oder false auszugeben, Sie können also false ausgeben (Sie erinnern sich, daß Sie true nur dann ausgeben, wenn diese Methode das Ereignis verarbeitet hat).

Zusätzliche Komplikationen können dann auftreten, wenn viele Komponenten mit denselben Klassen vorkommen - beispielsweise eine große Anzahl von Schaltflächen. Sie können alle Aktionen erzeugen und sind alle Instanzen von Button. Hier brauchen wir das zusätzliche Argument: Sie können die Labels, Elemente oder den Inhalt der Komponenten zur Bestimmung der das Ereignis erzeugenden Komponente und einfache Zeichenkettenvergleiche zur ihrer Auswahl verwenden. (Vergessen Sie dabei nicht, das Argument in den richtigen Objekt-Typ zu casten.)

public boolean action(Event evt, Object arg) {
   if (evt.target instanceof Button) {
      String labl = (String)arg;
      if (labl.equals("OK"))
         // OK-Schaltfläche handhaben 
      else if (labl.equals("Cancel"))
         // Cancel-Schaltfläche handhaben 
      else if (labl.equals("Browse"))
         // Browse-Schaltfläche handhaben 
      ...
}


Und wie ist das bei Kontroll- und Optionsfeldern? Da ihr zusätzliches Argument immer true ist, würde entsprechendes Testen nicht viel nützen. Im allgemeinen sollten Sie auf ein gewähltes Kontroll- oder Optionsfeld nicht reagieren. Normalerweise können Kontroll- und Optionsfelder frei vom Benutzer gewählt oder nicht gewählt werden, und ihre Werte werden dann an anderer Stelle überprüft (beispielsweise beim Anklikken einer Schaltfläche).

Um eine Reaktion Ihres Programms auf die Auswahl eines Kontroll- oder Optionsfelds zu initiieren, können Sie, anstatt das zusätzliche Argument zu verwenden, die Methode getLabel() benutzen, um das Label des Kontrollfelds im Rumpf der action()-Methode zu ermitteln. (Praktisch alle Komponenten verfügen über ähnliche Methoden; die Anwendung gestaltet sich nur leichter, wenn die Information als zusätzliches Argument übergeben wird.)

Im Abschnitt »Beispiel: Hintergrundfarbwechsler« erstellen Sie ein einfaches AWT-basiertes Applet, das Ihnen die Verwendung der action()-Methode in einer richtigen Anwendung zeigt.

Fokusereignisse behandeln

Wie bereits erwähnt, stellen Aktionsereignisse die bei weitem am häufigsten vorkommenden Ereignisse der Benutzeroberfläche dar, mit denen Sie sich im Hinblick auf die Komponenten, die in dieser Lektion behandelt wurden, beschäftigen werden. Allerdings können Sie vier weitere Ereignisarten in Ihrem eigenen Programm benutzen: Liste gewählt, Liste nicht gewählt, Fokuserhalt und Fokusverlust.

Für die Ereignisse Fokuserhalt und Fokusverlust können Sie die Methoden gotFocus() und lostFocus() verwenden, die in gleicher Weise wie action() eingesetzt werden. Hier ihre Signatur:

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

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

Für die Ereignisse Listeneintrag gewählt und Listeneintrag abgewählt stehen keine Methoden zur Verfügung, die Sie einfach überschreiben können. Für diese Ereignisse müssen Sie handleEvent()wie folgt verwenden:


public boolean handleEvent(Event evt) {
   if (evt.id == Event.LIST_SELECT)
      handleSelect(Event);
   else if (evt.id == Event.LIST_DESELECT)
      handleDeselect(Event);
   else return super.handleEvent(evt);
}

In diesem Codebruchstück sind Event.LIST_SELECT und Event.LIST_DESELECT die offiziellen Ereigniskennzeichen für die Ereignisse Liste gewählt und Liste nicht gewählt. Hier wurde die Kontrolle einfach an die zwei Handler-Methoden handleSelect() und handleDeselect() weitergegeben, die theoretisch an anderer Stelle definiert sind. Beachten Sie den Aufruf an super.handleEvent() in der unteren Zeile; dieser Aufruf sorgt dafür, daß andere Ereignisse elegant in der Hierarchie an die ursprüngliche handleEvent()-Methode weitergegeben werden.

Ereignisse von mehrzeiligen Textfeldern

Mehrzeilige Textfelder erzeugen dieselben Ereignisse wie Textfelder. Sie können die Methoden gotFocus() und lostFocus() verwenden, um Fokusereignisse zu verarbeiten:

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

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

Ereignisse von Listenfeldern

Listenfelder erzeugen drei verschiedene Ereignisarten: Die Auswahl bzw. Abwahl eines Eintrags in der Liste erzeugen entsprechende Ereignisse bzw. ein Doppelklick auf einen Eintrag erzeugt ein Aktionsereignis.

Sie können die Methode action() überschreiben, um einen Doppelklick auf einen Eintrag in einer Liste zu behandeln. Die Aus- bzw. Abwahl eines Eintrags in einer Liste behandeln Sie, indem Sie die Methode handleEvent() überschreiben und auf die Ereignis-ID LIST_SELECT und LIST_DESELECT prüfen.

Ereignisse von Bildlaufleisten

Wenn Sie den Umgang mit Ereignissen mögen, dann werden Sie Bildlaufleisten lieben. Eine ganze Reihe von Ereignissen wird allein von den verschiedenen Bewegungen innerhalb einer Bildlaufleiste erzeugt. Sie müssen die Methode handleEvent() für alle diese Ereignisse verwenden. In Tabelle 13.3 sind die Ereignis-IDs aufgeführt, auf die Sie prüfen müssen, und die Bewegung, die die Ereignisse auslöst.

Tabelle 13.3: Ereignisse von Bildlaufleisten

Ereignis-ID

Beschreibung

SCROLL_ABSOLUTE

Wird erzeugt, wenn der Schieber der Bildlaufleiste bewegt wird.

SCROLL_LINE_DOWN

Wird erzeugt, wenn die linke oder untere Schaltfläche der Bildlaufleiste angeklickt wird.

SCROLL_LINE_UP

Wird erzeugt, wenn die rechte oder obere Schaltfläche der Bildlaufleiste angeklickt wird.

SCROLL_PAGE_DOWN

Wird erzeugt, wenn der Bereich unter oder links von dem Schieber angeklickt wird.

SCROLL_PAGE_UP

Wird erzeugt, wenn der Bereich über oder rechts von dem Schieber angeklickt wird.

Beispiel: Hintergrundfarbwechsler

Aufgrund von Codebruchstücken allein die Zusammenhänge aller Teile zu verstehen, ist schwer. Machen wir uns also an die Lösung dieses Problems und erstellen ein einfaches AWT-Applet. Morgen, nachdem Sie mehr über die komplexeren Teile des AWT gelernt haben, werden wir uns mit einem etwas anspruchsvolleren Applet befassen, um den bisherigen Lerninhalt zu untermauern.

Das in Abbildung 13.4 dargestellte Applet, das Sie in diesem Abschnitt erstellen werden, verwendet fünf Schaltflächen, die übersichtlich oben am Bildschirm angeordnet sind, wobei jede mit dem Namen einer Farbe beschriftet ist. Jede Schaltfläche ändert die Hintergrundfarbe des Applets in die auf der Schaltfläche vermerkte Farbe.


Abbildung 13.4:
Das Applet SetBack

Der erste Schritt in diesem Abschnitt besteht allerdings darin, den Code der Benutzeroberfläche zu erzeugen. Im allgemeinen ist dies der beste Weg zur Erstellung eines AWT-basierten Applets: Erzeugen Sie die Komponenten und das Layout, und stellen Sie sicher, daß alles richtig aussieht, bevor Sie sich an die Einbindung der Ereignisse und damit an die eigentliche Arbeit mit dem Applet machen.

Bei diesem Applet sind die Komponenten und das Layout ausgesprochen einfach gehalten. Das Applet beinhaltet fünf einfache, oben am Bildschirm in einer Reihe angeordnete Schaltflächen. Ein FlowLayout eignet sich für diese Anordnung am besten und erfordert wenig Arbeit.

Hier der Code der für dieses Applet erzeugten Klassenstruktur und init()-Methode. Das FlowLayout ist zentriert, und jede Schaltfläche hat einen Abstand von 10 Punkten. Anschließend müssen Sie lediglich die einzelnen Schaltflächen erstellen und hinzufügen.

import java.awt.*;

public class SetBack extends java.applet.Applet {

    Button redButton,blueButton,greenButton,
        whiteButton,blackButton;

    public void init() {
        setBackground(Color.white);
        setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));

        redButton = new Button("Red");
        add(redButton);
        blueButton = new Button("Blue");
        add(blueButton);
        greenButton = new Button("Green");
        add(greenButton);
        whiteButton = new Button("White");
        add(whiteButton);
        blackButton = new Button("Black");
        add(blackButton);
        }

Auf den ersten Blick sieht dieser Code wahrscheinlich umfangreicher als notwendig aus. Sie könnten dagegen setzen, daß man zur Unterbringung der Schaltflächen eigentlich nicht alle Instanzvariablen benötigt. Das Ganze ist in der Tat ein bißchen undurchsichtig; da das Applet bereits erstellt ist, ist die geeignete Schreibweise bekannt, und die Instanzvariablen sorgen später für Erleichterung (haben Sie Vertrauen). Es wird häufiger vorkommen, daß, wenn Sie Ihre eigenen Applets schreiben, der von Ihnen ursprünglich für die Benutzeroberfläche geschriebene Code nicht gut funktioniert und Sie ihn entsprechend ändern müssen. Das macht überhaupt nichts! Je mehr Applets Sie schreiben, desto leichter werden Sie verstehen, wie letztendlich alles zusammenpaßt.

Ereigniscode einfügen

Das Anklicken von Schaltflächen löst Aktionsereignisse aus. Wie Sie bereits wissen, verwenden Sie zur Handhabung eines Aktionsereignisses die Methode action(). Diese action()-Methode wird hier folgendes auslösen:

Bevor wir uns dem Schreiben der action()-Methode widmen, ist noch eine weitere designbezogene Entscheidung zu treffen. Im wesentlichen sind die letzten drei Schritte - bis auf kleine Unterschiede - für jede Schaltfläche identisch, so daß es wirklich Sinn macht, sie in einer eigenen Methode unterzubringen. Sie heißt changeColor() und wird zur Vereinfachung der Logik in action() beitragen.

Auf dieser Grundlage gestaltet sich die action()-Methode einfach:

public boolean action(Event evt, Object arg) {
  if (evt.target instanceof Button) {
    changeColor((Button)evt.target);
    return true;
  } else return false;
}

Diese action()-Methode unterscheidet sich wenig von den einfachen, im Abschnitt über Aktionen erzeugten. Der erste Schritt beinhaltet die Verwendung von evt.target , um sicherzustellen, daß die Komponente eine Schaltfläche ist. An dieser Stelle geben Sie die Kontrolle an die noch zu schreibende Methode changeColor() weiter und geben true aus. Falls das Ereignis keine Schaltfläche ist, geben Sie false aus.

Beachten Sie das eine Argument für changeColor(). Mit diesem Argument geben Sie das eigentliche Schaltflächenobjekt, das das Ereignis erhalten hat, an die Methode changeColor() weiter. (Das Objekt in evt.target ist eine Instanz der Klasse Object; es muß also in einen Button-Objekt gecastet werden, damit Sie es als Schaltfläche verwenden können.) Ab hier wird die Methode changeColor() dies handhaben.

Apropos changeColor(), machen wir weiter und definieren jetzt diese Methode. Die Methode changeColor() ist hauptsächlich darauf ausgerichtet festzustellen, welche Schaltfläche angeklickt wurde. Sie erinnern sich, daß das zusätzliche Argument bei action() das Label der Schaltfläche war. Obwohl Sie mit einem Zeichenkettenvergleich in changeColor() herausfinden können, welche Schaltfläche angeklickt wurde, stellt das nicht die eleganteste Lösung dar und macht Ihren Ereigniscode in zu starkem Maße von der grafischen Benutzeroberfläche abhängig. Falls Sie ein Schaltflächen-Label ändern möchten, müssen Sie noch einmal zurückgehen und auch Ihren Ereigniscode neu bearbeiten. In diesem Applet können Sie somit das zusätzliche Argument vollkommen ignorieren.

Wie wissen Sie nun, welche Schaltfläche angeklickt wurde? An dieser Stelle kommen die Istanzvariablen der Schaltfläche ins Spiel. Das in der Instanzvariablen target des Ereignisses enthaltene Objekt - das Sie an changeColor() weitergegeben haben - ist eine Instanz von Button, und eine dieser Instanzvariablen enthält eine Referenz zu genau demselben Objekt. Sie müssen die beiden nur in changeColor() vergleichen, um zu sehen, ob sie dasselbe Objekt darstellen, den Hintergrund einstellen und neu zeichnen, und zwar folgendermaßen:

void changeColor(Button b) {
    if (b == redButton) setBackground(Color.red);
    else if (b == blueButton) setBackground(Color.blue);
    else if (b == greenButton) setBackground(Color.green);
    else if (b == whiteButton) setBackground(Color.white);
    else setBackground(Color.black); 

     repaint();
  }

Anklicken einer Schaltfläche von der Benutzeroberfläche aus ruft action()auf, action() ruft changeColor() auf und changeColor() stellt den entsprechenden Hintergrund ein. Ganz einfach! Listing 13.4 zeigt das fertige Applet.

Listing 13.4: Der gesamte Quelltext von SetBack.java

 1: import java.awt.*;
 2:
 3: public class SetBack extends java.applet.Applet {
 4:
 5:     Button redButton,blueButton,greenButton,whiteButton,blackButton;
 6:
 7:     public void init() {
 8:         setBackground(Color.white);
 9:         setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
10:
11:         redButton = new Button("Red");
12:         add(redButton);
13:         blueButton = new Button("Blue");
14:         add(blueButton);
15:         greenButton = new Button("Green");
16:         add(greenButton);
17:         whiteButton = new Button("White");
18:         add(whiteButton);
19:         blackButton = new Button("Black");
20:         add(blackButton);
21:     }
22:
23:     public boolean action(Event evt, Object arg) {
24:         if (evt.target instanceof Button) {
25:             changeColor((Button)evt.target);
26:             return true;
27:         } else return false;
28:     }
29:
30:     void changeColor(Button b) {
31:         if (b == redButton) setBackground(Color.red);
32:         else if (b == blueButton) setBackground(Color.blue);
33:         else if (b == greenButton) setBackground(Color.green);
34:         else if (b == whiteButton) setBackground(Color.white);
35:         else setBackground(Color.black);
36:
37:         repaint();
38:     }
39: }

Sie können dieses Applet testen, indem Sie den folgenden HTML-Code verwenden:

<applet code="SetBack.java" width=200 height=200>
</applet>

Zusammenfassung

Die Beendigung der Arbeit des heutigen Tages stellt in Ihrer Java-Programmierkarriere ein großes Ereignis dar. Die Fähigkeit, Ereignisse zu verarbeiten, macht es möglich, voll funktionsfähige Applets mit einer grafischen Benutzeroberfläche zu schreiben, die zur Interaktion mit dem Benutzer verwendet werden kann.

Morgen werden Sie Ihr Wissen um das Abstract Windowing Toolkit mit einem ausgefeilteren Projekt vertiefen. Außerdem werden Features, wie z.B. eigenständige Fenster, behandelt.

Während der dritten Woche erhalten Sie die Chance, eine funktionierende Applikation, die Swing - das neue Paket für grafische Benutzeroberflächen, das mit Java 1.2 eingeführt wurde - verwendet, zu erstellen.

Fragen und Antworten

Frage:
Ich habe eine neue Schaltflächenklasse, die ich im Aussehen unterschiedlich zu den Standard-AWT-Schaltflächenobjekten in 1.02 definiert habe. Ich möchte Aufrufe auf diese Schaltfläche rückführen (d.h. eine arbiträre Funktion bei Anklicken der Schaltfläche ausführen), ich kann aber nicht herausfinden, wie ich mit Java eine arbiträre Methode ausführen kann. In C++ verfüge ich nur über einen auf eine Funktion weisenden Zeiger. In Small-Talk würde ich perform verwenden: Wie kann ich das in Java durchführen?

Antwort:
In Java 1.02 können Sie es nicht; Aktionen der Schaltfläche werden durch ein action-Ereignis getriggert, das in derselben Klasse wie die Schaltfläche enthalten sein muß. Sie müssen Ihre Schaltflächenklasse jedesmal ableiten, wenn Sie für diese Schaltfläche ein anderes Verhalten erzeugen möchten. Dies ist einer der Gründe dafür, daß das Ereignismodell nach Java 1.02 geändert wurde; es ist wesentlich einfacher und effizienter, Ihre eigenen Komponenten zu erzeugen, wenn der Ereigniscode nicht zu stark an den Code der grafischen Benutzeroberfläche gebunden ist.



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


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