Ein wichtiger Bestandteil der Programmierung von Oberflächen ist das Verarbeiten von Ereignissen, die durch Aktionen des Benutzers ausgelöst werden. Beispiele von Aktionen, die Ereignisse auslösen, sind das Anklicken eines Buttons, das Auswählen eines Eintrages in einer Auswahlliste usw. Auf diese Ereignisse soll das Applet reagieren. Zu diesem Zweck erlaubt es das Event-Modell von Java, sogenannte Listener zu spezifizieren. Deren Aufgabe ist es, auf bestimmte Ereignisse zu warten (zu lauschen) und im Fall des Auftretens des Ereignisses eine bestimmte Aktion auszuführen.
Ausgangspunkt ist eine Komponente y des Applets, für die auf ein bestimmtes Ereignis reagiert werden soll. Zu jeder Komponente eines Applets und zum Applet selbst ist eine Menge von Ereignissen definiert, die zu dieser Komponente auftreten können. Zu einem oder mehreren dieser Ereignisse kann ein Listener implementiert und an das Element angehängt werden. Dazu gibt es vordefinierte Schnittstellen, die Listener-Schnittstellen, die festlegen, welche Methoden in diesem Fall zu implementieren sind. Im folgenden wird Schritt für Schritt die Vorgehensweise bei der Definition eines geeigneten Listeners beschrieben:
ActionEvent).ActionListener): In diesem Fall
definiere eine neue Listener-Klasse, die diese
Schnittstelle implementiert. Dazu muß im
wesentlichen die Methode der Schnittstelle realisiert
werden. Die Listener-Klasse wird als innere Klasse des
Applets definiert, d.h. als Klasse in der Klasse.
Für das Beispiel ActionListener sieht das
wie folgt aus, wobei MyListener die
neu definierte innere Klasse ist.
... // inside the applet
class MyListener implements ActionListener {
public void actionPerformed (ActionEvent e) {
// define what should be done when the event occurs
}
}// class MyListener
... // rest of the applet
Die Reaktion auf das Anklicken eines Buttons wird z.B. auf diese Weise realisiert (siehe Beispiel).
MouseListener): In diesem
Fall gibt es zusätzlich eine zugehörige
Adapter-Klasse. Die Adapter-Klasse sieht
Standardimplementierungen zu allen Methoden vor und
vermeidet somit, daß man alle
Methoden des Listeners selbst implementieren muß. Es
genügt also wiederum, nur die
Methode zu implementieren, die man aktuell braucht. Diesmal
wird allerdings eine innere
Klasse implementiert, die von der Adapter-Klasse erbt.
... // inside of the applet
class MyMouseListener
extends MouseAdapter
{
public void mousePressed (MouseEvent m) {
// define what should be done when the event occurs
}// mousePressed
}
... // rest of the applet
Nach diesem Schritt hat man eine innere Klasse MyXListener.
addActionListener oder
addMouseListener).
Für ein ActionEvent sieht das wie folgt aus:
public void init () {
add (y) ;
...
y.addActionListener (new ChangeButtonListener ()) ;
}
import java.awt.event.* ;
Tabelle 1: Elemente und die zugehörigen Ereignisse (Events)
| Applet, Panel | Button | TextField | TextArea |
| ContainerEvent FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
ActionEvent, TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
| Label | Checkbox | Choice | List |
| FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
ActionEvent, ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent |
Weitere Ereignisklassen, die hier nicht betrachtet werden sind AdjustmentEvent und WindowEvent.
Tabelle 2: Ereignisse und die zugehörigen Listener-Schnittstellen (und Adapter-Klassen)
| Ereignis | Listener-Schnittstelle | Adapter-Klasse | Methoden der Schnittstelle |
| ActionEvent | ActionListener | keine | actionPerformed(ActionEvent) |
| ComponentEvent | ComponentListener | ComponentAdapter | componentHidden(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent) componentShown(ComponentEvent) |
| ContainerEvent | ContainerListener | ContainerAdapter | componentAdded(ContainerEvent) componentRemoved(ContainerEvent) |
| FocusEvent | FocusListener | FocusAdapter | focusGained(FocusEvent) focusLost(FocusEvent) |
| KeyEvent | KeyListener | KeyAdapter | keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) |
| MouseEvent | MouseListener | MouseAdapter | mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) |
| MouseMotionListener | MouseMotionAdapter | mouseDragged(MouseEvent) mouseMoved(MouseEvent) |
|
| ItemEvent | ItemListener | keine | itemStateChanged(ItemEvent) |
| TextEvent | TextListener | keine | textValueChanged(TextEvent) |
Typisch für die Verwendung eines Buttons ist es, auf
das Ereignis, daß der Button angeklickt wird, reagieren
zu wollen. Dabei wird ein ActionEvent erzeugt, so
daß man die Schnittstelle ActionListener
implementieren muß, um darauf zu reagieren.
Diese Schnittstelle enthält nur eine Methode, und zwar
actionPerformed, die implementiert werden muß.
Dazu definiert man eine innere Klasse innerhalb des Applets. In
dem folgenden Beispiel wird die Beschriftung des Buttons
b verändert, wenn ein Ereignis vom Typ
ActionEvent für diesen Button auftritt.
public class ChangeButton
extends Applet
{
Button b = new Button (...) ;
// applet methods:
public void init () {
add (b) ;
...
b.addActionListener (new ChangeButtonListener ()) ;
}
// inner class:
class ChangeButtonListener implements ActionListener {
public void actionPerformed (ActionEvent e) {
...
b.setLabel (...) ;
}
} // end of inner class
} // end of applet class
Das vollständige Beispiel zeigt einen Button, dessen Beschriftung zeigt, wie oft er angeklickt worden ist.
Typisch für ein Listenobjekt ist es, den
ItemListener zu verwenden. Er wird
bei einem ItemEvent ausgeführt. Bei einer Liste
tritt dieses Ereignis auf, wenn ein Eintrag der Liste seinen
Zustand ändert, d.h. von selektiert zu deselektiert oder
umgekehrt.
Die Schnittstelle ItemListener enthält nur
eine Methode, nämlich itemStateChanged,
die bei der Definition eines Listeners implementiert werden
muß. Zu diesem Zweck
definiert man im Applet eine innere Klasse, die die
Schnittstelle ItemListener implementiert. Das
folgende Beispiel zeigt die Definition einer solchen inneren
Klasse.
public class EisAuswahl extends Applet {
List list = new List (3, true) ;
TextArea t = new TextArea (5, 20) ;
public void init () {
...
add (list) ;
list.addItemListener (new EisListener ()) ;
add(t);
}
class EisListener implements ItemListener {
public void itemStateChanged (ItemEvent ie) {
t.setText ("") ;
String [] items = list.getSelectedItems () ;
for (int i = 0; i < items.length; i++)
t.append (items [i] + "\n") ;
}
} // end of inner class
} // end of applet
Durch den Aufruf der Methode getSelectedItems kann
man auf die aktuell
selektierten Einträge der Liste zugreifen
(hier: list.getSelectedItems ()). Als
Rückgabewert erhält man ein Array mit Strings, die man
weiterverarbeiten kann. Im obigen
Beispiel werden sie mit einer for-Schleife in ein
dafür vorgesehenes Objekt der
Klasse TextArea eingefügt. Zu dieser Art
Listener existiert auch ein
vollständiges Beispiel.
Wenn nur ein Eintrag der Liste selektiert werden kann, kann auf
dieses mit der Methode getSelectedItem zugegriffen
werden, die einen String zurückliefert.
Wie bei den Listen sind auch bei den Check Boxes die wichtigsten
Ereignisse die der
Klasse ItemEvent. Dieses Ereignis tritt auf, wenn
eine Checkbox angeklickt wird.
Es ist also wieder die Schnittstelle ItemListener zu
implementieren. Check Boxes gehören oft in Gruppen
zusammen, insbesondere wenn es sich um Radio Buttons handelt.
Für solche Gruppen gibt es prinzipiell zwei Ansätze:
if-Abfrage notwendig, um
festzustellen, für welche der Check Boxes
das Ereignis aufgetreten ist. Die erste Variante ist besonders dann geeignet, wenn man je nachdem, welche Box ausgewählt worden ist, unterschiedliche Operationen ausführen möchte. Sie kann aber zu einer hohen Zahl von inneren Klassen führen.
Das folgende Beispiel zeigt die zweite Variante. Die für die Ereignisbehandlung notwendigen Teile sind rot markiert.
public class Lieblingseis extends Applet {
...
CheckboxGroup cg = new CheckboxGroup () ;
Checkbox c1 = new Checkbox ("Heidelbeere", cg, false),
c2 = new Checkbox("Erdbeere", cg, false),
... ;
TextField t = new TextField (...) ;
public void init () {
add (c1) ;
c1.addItemListener (new EisListener ()) ;
add (c2) ;
c2.addItemListener (new EisListener()) ;
...
add (t) ;
}
class EisListener implements ItemListener {
public void itemStateChanged (ItemEvent ie) {
t.setText ((String)ie.getItem ()) ;
}
}
}
Über den Parameter ie vom Typ
ItemEvent steht der Methode
itemStateChanged zur Zeit des Aufrufes ein
Handle für das eingetretene Ereignis zur
Verfügung. Für Objekte der Klasse
ItemEvent sind eine Reihe von Methoden
definiert. Dazu zählt auch die parameterlose Methode
getItem, die das von der
Methode betroffene Objekt zurückliefert. Bei Check Boxes
handelt es sich dabei (nach einer Konvertierung zu
String) um die Zeichenkette, die zur
Beschriftung der Check Box verwendet wurde. Der Ausdruck
(String)ie.getItem ()
liefert also im konkreten Beispiel die jeweilige Eissorte zurück. Das Textfeld wird verwendet, um die selektierte Eissorte anzuzeigen (siehe auch vollständiges Beispiel).
Neben den konkreten Aktionen, die durch das Bedienen der
interaktiven Komponenten eines
Applets, wie z.B. das Drücken eines Buttons oder die
Auswahl eines Eintrages, angestoßen werden, löst
auch die Bewegung der Maus und das Betätigen der
Maus-Buttons Ereignisse aus, wenn sich die
Maus innerhalb des Applets befindet. Um diese Ereignisse
verarbeiten zu können, gibt es die Ereignisklasse
MouseEvent. Wie Tabelle 1 zu entnehmen ist,
werden Ereignisse dieser Klasse für alle betrachteten
Komponenten unterstützt.
Zur Behandlung von Mausereignissen sind zwei verschiedene
Schnittstellen MouseMotionListener
und MouseListener definiert (mit den
zugehörigen Adapterklassen).
MouseMotionListener ist für Bewegungen der
Maus zuständig und enthält zwei Methoden
mouseMoved und mouseDragged:
mouseMoved ist zu
implementieren, wenn bei Bewegung der Maus eine
bestimmte Aktion ausgeführt werden soll.mouseDragged ist zu
implementieren, wenn bei Bewegung der Maus mit
gedrückter Taste eine bestimmte Aktion
ausgeführt werden soll.MouseListener ist für die Maustasten
zuständig und enthält dafür drei
Methoden mouseClicked,
mousePressed und mouseReleased.
Weiterhin können auch Aktionen dafür
definiert werden, daß die Maus in eine bestimmte
Komponente eintritt oder sie verläßt.
Dafür gibt es die Methoden mouseEntered
und mouseExited.
Beide Schnitstellen haben mehr als einen Methode, so daß hier in der Regel mit den zugehörigen Adapter-Klassen gearbeitet wird (s. Vorgehensweise).
Im folgenden Beispiel werden die Mausbewegungen in einem
Panel paintPanel als Ereignisse verarbeitet. Zu
diesem Zweck wird eine innere Klasse
MyMotionListener als Subklasse von
MouseMotionAdapter implementiert. In
MyMotionListener wird die Methode Methode
mouseMoved redefiniert, da auf Mausbewegungen
reagiert werden soll.
public class MouseExample extends Applet {
Panel monitor = new Panel (),
paintPanel = new Panel () ;
TextField newPosition = new TextField (20) ;
...
public void init () {
setLayout (new BorderLayout ()) ;
add (BorderLayout.CENTER, paintPanel) ;
paintPanel.addMouseMotionListener (new MyMotionListener ()) ;
add(BorderLayout.SOUTH, monitor);
...
monitor.add(newPosition);
}
class MyMotionListener extends MouseMotionAdapter {
int x = 0, y = 0 ;
public void mouseMoved (MouseEvent m) {
...
x = m.getX () ;
y = m.getY () ;
newPosition.setText ("x = " + x + " y = " + y) ;
}
}// class MyMotionListener
}// class MouseExample
Über den Parameter m vom Typ
MouseEvent besitzt die Methode ein
Handle für das eingetretenen Ereignis. Die
Methoden getX () und getY () sind
für Objekte des Typs MouseEvent definiert
und erlauben es, auf die X und Y-Koordinate der
Mausposition zuzugreifen. Im Beispiel wird die jeweilige
Mausposition in das Textfeld newPosition
ausgegeben. Im
vollständigen Beispiel
wird jeweils auch die vorherige Position ausgegeben, die
in den Variable x und y zwischengespeichert wird.
Das vorherige Beispiel wird jetzt um Aktionen erweitert,
die ausgeführt werden, wenn
innerhalb der Komponente paintPanel die
Maustaste gedrückt bzw. wieder
losgelassen wird. Dazu wird eine innere Klasse
MyMouseListener als Subklasse der
Klasse MouseAdapter implementiert. In dieser
Klasse werden die Methoden mousePressed
bzw. mouseReleased redefiniert. Im Beispiel
zeichnen diese Methoden an der aktuellen Position einen
roten Kreis (mousePressed) bzw. einen
schwarzen Punkt (mouseReleased).
Der Effekt kann am
vollständigen Beispiel
beobachtet werden.
public class MouseExample extends Applet {
Panel paintPanel = new Panel () ;
...
public void init () {
setLayout (new BorderLayout ()) ;
add (BorderLayout.CENTER, paintPanel) ;
paintPanel.addMouseListener (new MyMouseListener ()) ;
...
}
class MyMouseListener
extends MouseAdapter
{
public void mousePressed (MouseEvent m) {
Graphics g = paintPanel.getGraphics () ;
g.setColor (Color.red) ;
g.drawOval (m.getX (), m.getY (), 20, 20) ;
}
public void mouseReleased (MouseEvent m) {
Graphics g = paintPanel.getGraphics () ;
g.setColor (Color.black) ;
g.fillOval (m.getX (), m.getY (), 10, 10) ;
}
}// class MyMouseListener
}// class MouseExample
Listener können nicht nur in der
init-Methode an die Komponenten angehängt
werden, sondern auch zu anderen Zeitpunkten. Insbesondere
ist es auch möglich, innerhalb einer Listener-Methode
einen Listener an eine Komponente anzuhängen oder von einer
Komponente wegzulöschen. Damit ist es möglich,
festzulegen, daß eine Komponente x erst dann
anfängt oder aufhört auf ein bestimmtes Ereignis
zu reagieren, wenn vorher ein anderes Ereignis für
diesselbe oder eine andere Komponente eingetreten ist.
Zum Löschen eines Listeners XListener wird
die Methode removeXListener verwendet.
Im folgenden Beispiel wurde die Eisauswahl um einen
Schalter (switch) erweitert, mit dem man die Eisauswahl
an- und ausschalten kann: Nur wenn der Schalter
angeschaltet ist, hat die Auswahl von Eissorten einen
Effekt. Dazu wurde ein Knopf mit einem
ActionListener eingefügt. Erst wenn
dieser Knopf gedrückt wird (Schalter an), wird der
EisListener an die Eisliste
angehängt. Bei erneutem Anklicken des Knopfes
(Schalter aus) wird der Listener wieder gelöscht.
public class Switch extends Applet {
Button b = new Button ("off") ;
List list = new List (3, true) ;
...
public void init () {
...
add (b) ;
b.addActionListener (new SwitchListener ()) ;
list.addItem ("Heidelbeere") ;
...
add (list) ;
}
class EisListener
implements ItemListener
{
public void itemStateChanged (ItemEvent ie) {
...
}
}// class EisListener
class SwitchListener
implements ActionListener
{
ItemListener il = new EisListener () ;
public void actionPerformed (ActionEvent e) {
if ( b.getLabel ().equals ("off") ) {
list.addItemListener (il) ;
b.setLabel ("on") ;
}
else {
list.removeItemListener (il) ;
...
b.setLabel ("off") ;
}
}
}// class SwitchListener
}// class Switch
Der Effekt des Schalters kann am vollständigen Beispiel untersucht werden.
Ergänze das Applet PaintTool um eine geeignete Ereignisbehandlung
itemStateChanged dieser
Schnittstelle, so daß sie ein Datenfeld
isForeground vom Typ Boolean
Deines Applets entsprechend umsetzt.
translate
der Klasse ColorChoice, um von dem
selektierten Farbnamen (String) zur Farbe
vom Typ Color zu übersetzen.Zusatzaufgabe (freiwillig):
Ein Problem dieses Applets ist, daß das Zeichnen der Linie immer im Ursprung beginnt. Um dies zu umgehen, kann das Applet so modifiziert werden, daß der Listener erst dann angehängt wird, wenn die Maustaste gedrückt wird. Dabei wird die aktuelle Mausposition für die Initialisierung von (xPosition, yPosition) übergeben. Dazu muß eine zusätzliche Listener-Klasse geschrieben und in der inneren Klasse MyMotionListener ein Konstruktor definiert werden, der die x und y-Koordinate als Parameter erhält. Beim Loslassen der Maustaste wird der Listener wieder gelöscht.
| Home | hw.sehring, apr-2002 |
|---|