|
Objektorientiertes Programmieren mit C++ und JAVA
|
|  
|
32.2 Events in JAVA 1.1
|  
|
Ab Version 1.1 ist man einen völlig anderen Weg zur Auswertung von
Ereignissen gegangen. Es gibt eine Vielzahl verschiedener Event-Klassen,
eine für jedes Ereignis. Alle sind von EventObject aus
java.util abgeleitet, die uns interessierenden von der Unterklasse
AWTEvent aus java.awt.
EventObject dient nur dazu, den Urheber des Ereignisses zu codieren:
- public class EventObject implements java.io.Serializable
{
public EventObject(Object source);
public Object getSource();
public String toString();
}
AWTEvent ist abstrakt, speichert aber schon Informationen über die
Art des Ereignisses, die man mit getID abfragen kann:
- public abstract class AWTEvent extends EventObject
{
public AWTEvent(Event event);
public AWTEvent(Object source, int id);
Event convertToOld();
public int getID();
protected void consume();
protected boolean isConsumed();
// ...
}
Es werden nicht mehr alle Ereignisse an alle Komponenten gemeldet (und
dort ggf. ignoriert). Stattdessen müssen die Komponenten, die an
bestimmten Ereignissen interessiert sind, ihr Interesse anmelden. Eine
solche Komponente heißt Event Listener, eine erzeugende
Komponente Event Source.
Source ist beim AWT immer eine AWT-Component.
Man kann das Interesse wieder abmelden oder nachträglich
anmelden.
Wenn ein Ereignis eintritt, ruft die Source-Component eine
(Event-abhängige) bestimmte Methode in jedem angemeldeten Listener auf.
Dazu müssen die Listener ein jeweils spezielles Interface implementieren.
Diese Interfaces sind alle von java.util.EventListener abgeleitet und
im Package java.awt.event gesammelt.
Einige Interfaces fordern nur eine einzige Methode, die meisten mehrere.
Beispielsweise muß MouseListener implementiert werden, wenn man
über Maus-Ereignisse informiert werden will:
- public interface MouseListener extends EventListener
{
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);
}
Wenn man MouseListener implementiert, muß man alle fünf
Methoden implementieren. Wenn man beispielsweise nur an einfachen Mausklicks
interessiert ist, kann man besser eine Adapter-Klasse aus
java.awt.event beerben, in der alle fünf Methoden leer
implementiert sind:
- public abstract class MouseAdapter implements MouseListener
{
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) {}
}
Man überschreibt dann nur die eine Methode, die einen interessiert,
etwa mouseClicked(). Für alle Listener mit mehr als einer
Methode gibt es eine solche Adapter-Klasse mit entsprechendem Namen.
In der folgenden Tabelle sind die Event-Klassen, die zugehörigen
Interfaces und die darin enthaltenen Methoden aufgeführt.
| Event-Klasse | Listener-Interface | Methoden im Listener
ActionEvent
ActionListener
actionPerformed()
AdjustmentEvent
AdjustmentListener
adjustmentValueChanged()
ComponentEvent
ComponentListener
componentHidden(), componentMoved(),
componentResized(), componentShown()
ContainerEvent
ContainerListener
componentAdded(), componentRemoved()
FocusEvent
FocusListener
focusGained(), focusLost()
ItemEvent
ItemListener
itemStateChanged()
KeyEvent
KeyListener
keyPressed(), keyReleased(), keyTyped()
MouseEvent
MouseListener
mouseClicked(), mousePressed(),
mouseReleased(), mouseEntered(),
mouseExited()
|  
| MouseMotionListener
mouseDragged(), mouseMoved()
TextEvent
TextListener
textValueChanged()
WindowEvent
WindowListener
| windowActivated(), windowClosed(),
windowClosing(), windowDeactivated(),
windowOpened(), windowIconified(),
windowDeiconified()
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
Das Registrieren dieser Listener läuft wie folgt ab: Jede Komponente,
die ein bestimmtes Ereignis auslösen kann, stellt eine
addListener- und eine removeListener-Methode zur
Verfügung. Beispielsweise löst die
Komponente Scrollbar einen AdjustmentEvent aus, wenn der
Benutzer sie bedient. Entsprechend definiert Scrollbar folgende
Methoden:
- void addAdjustmentListener(AdjustmentListener);
void removeAdjustmentListener(AdjustmentListener);
Beispiel:
Wir implementieren zunächst eine Fensterklasse, die an die Stelle eines
Mausklicks einen kleinen Kreis zeichnet. Das Beispiel kann dann leicht
so erweitert werden, daß es sich wie unser Applet aus
Abschnitt 32.1 verhält.
Wir arbeiten hier ohne Pufferung, d.h. die Punkte verschwinden, sobald
das Fenster verdeckt oder in der Größe geändert wird.
Wenn beim Klicken die Shift-Taste gedrückt ist, wird das Fenster
geschlossen und das Programm damit beendet.
Wir leiten unsere Klasse von Frame ab und können daher nicht
auch von MouseAdapter erben, müssen also alle fünf Funktionen
aus MouseListener implementieren.
- import java.awt.*;
import java.awt.event.*;
class ClickMeFrame extends Frame implements MouseListener
{
ClickMeFrame(int w, int h)
{
super("Click me!");
setSize(w,h); // d.h. Component.setSize()
addMouseListener(this); // d.h. Component.addMouseListener()
show(); // d.h. Window.show()
}
public void mousePressed(MouseEvent e)
{
if (e.isShiftDown()) dispose(); // d.h. Frame.dispose()
else
{
Graphics g=getGraphics(); // d.h. Component.getGraphics()
g.setColor(Color.black);
g.fillOval(e.getX(),e.getY(),10,10);
}
}
public void mouseClicked(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
}
public class listen
{
public static void main(String[] args)
{
new ClickMeFrame(256,256);
}
}
Die Listener lassen sich am elegantesten mit anonymen Klassen implementieren.
Es ist eben oft der Fall, daß spezielle Unterklassen der Adapter
nur ein einziges Mal benötigt werden. Man definiert sie deshalb am besten
direkt im Aufruf von addMouseListener().
Beispiel:
Wir leiten hier von MouseAdapter ab und überschreiben nur
mousePressed(). Unsere Unterklasse bekommt keinen Namen und wird direkt
im new-Aufruf ihres einzigen Objekts definiert. Die Oberklasse
wird hinter new angegeben, die überschreibenden Methoden
in einem normalen Klassen-Rumpf.
- class ClickMeFrame extends Frame
{
ClickMeFrame(int w, int h)
{
super("Click me!");
setSize(w,h);
addMouseListener
(
new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
if (e.isShiftDown())
dispose();
else
{ Graphics g=getGraphics();
g.setColor(Color.black);
g.fillOval(e.getX(),e.getY(),10,10);
}
}
}
);
show();
}
}
Beispiel:
Wir erzeugen mehrere Instanzen unserer Fensterklasse, im Testprogramm
vier Stück. Wir übergeben dem Konstruktor daher nun zusätzlich
die Startposition.
|
Wir wollen nun statt mit Shift-Maustaste für das Schließen eines
Fensters korrekt auf den (systemabhängigen) Schließ-Knopf reagieren.
Wir fügen dazu jedem Frame
einen WindowListener hinzu, indem wir von WindowAdapter
ableiten und nur windowClosing() überschreiben.
Unser Listener schließt auf jeden Fall den Frame.
Außerdem wird intern mitgezählt, wieviele Fenster noch offen sind.
Falls unser Fenster das letzte war, beenden wir
das Programm mit System.exit().
Als Erweiterung bauen wir noch vier Buttons ein, mit denen man die Farbe der
Kreise einstellen kann. Den Buttons fügen wir einen ActionListener
hinzu.
Wenn ein Farbknopf betätigt wird, speichern wir seine Nummer im
Datenmember numcol. Dazu muß der ActionListener die
Nummer des Knopfs kennen. Die Knöpfe werden in einer Schleife über
i erzeugt.
|
|
Damit die anonyme Klasse auf dieses i zugreifen kann, legen
wie eine finale Kopie icopy im Schleifenrumpf an.
- class ClickMeFrame extends Frame
{
static String text[]={ "black","red","green","blue" };
Color colors[]=new Color[]
{ Color.black, Color.red, Color.green, Color.blue };
int numcol;
static int count=0;
ClickMeFrame(int x, int y, int w, int h)
{
super("Click me!");
setLayout(new FlowLayout());
addWindowListener
(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
dispose();
if (--count==0) System.exit(0);
}
}
);
for (int i=0;i<=3;++i)
{
final int icopy=i;
Button b=new Button(text[i]);
b.addActionListener
(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
numcol=icopy;
}
}
);
add(b);
}
addMouseListener
(
new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
Graphics g=getGraphics();
g.setColor(colors[numcol]);
g.fillOval(e.getX()-5,e.getY()-5,10,10);
}
}
);
setSize(w,h);
setLocation(x,y);
show();
++count;
}
}
public class listen4
{
public static void main(String[] args)
{
new ClickMeFrame(0,0,256,256);
new ClickMeFrame(260,0,256,256);
new ClickMeFrame(0,260,256,256);
new ClickMeFrame(260,260,256,256);
}
}
Im Moment haben wir jedem Button einen eigenen ActionListener
spendiert. Man kann auch mit einem einzigen auskommen. Dieser muß dann
feststellen, welcher Button den Event ausgelöst hat. Dazu kann er
beispielsweise die Beschriftung des Buttons auslesen -- sie wird mit
in den Event gepackt und kann mit getActionCommand() herausgelesen
werden:
- ActionListener al=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String s=e.getActionCommand();
if (s.equals("black")) numcol=0;
else if (s.equals("red")) numcol=1;
else if (s.equals("green")) numcol=2;
else numcol=3;
}
};
for (int i=0;i<=3;++i)
{
Button b=new Button(text[i]);
b.addActionListener(al);
add(b);
}
Es gibt in JAVA 1.1 noch ein anderes Event-Model, das nicht
für die Applikations-Ebene (also für Fenster oder Applets) gedacht
ist, sondern für die Implementation von Komponenten wie speziellen
Knöpfen, Menüs, etc. Die Handhabung wäre mit dem
"großen" Modell manchmal zu umständlich.
Mit enableEvents() kann man für die eigene Komponente eine
Bitmaske angeben, die bestimmt, welche Arten von Events überhaupt bei ihr
ankommen sollen.
Dann fängt man die AWTEvents ab, bevor sie bei den Listenern
ankommen, indem man die Methode processKeyEvent(),
processMouseEvent(), etc. der Komponente überschreibt.
Dort findet sonst das Verteilen auf die Listener statt.
Wenn man nichts mit dem Event anfangen kann, gibt man ihn mit
super. an die Methode der Oberklasse weiter.