Grundprinzipien der ereignisgesteuerten
Programmierung
In diesem Skript geht es um die Grundlagen, die für das Verständnis und für die
eigenständige Entwicklung ereignisgesteuerter Java-Applikationen notwendig sind. Nach
erfolgreicher Beschäftigung mit dem Skript sollten Sie in der Lage sein,
· zu definieren, was man unter einem Ereignis versteht.
· zu erläutern, aus welchen 3 Grundbausteinen jedes ereignisgesteuerte Programm
besteht und wie diese Grundbausteine ineinander greifen.
· auf der Basis des Observer-Patterns mit Hilfe der Klassen Observable und
Observer eigene ereignisgesteuerte Applikationen zu entwickeln.
· erste Applikationen mit GUI-Events zu programmieren.
Das Skript orientiert sich dabei u.a. an den Ausführungen aus
· G.Krüger: Handbuch der Java-Programmierung
· C.Ullenboom: Java ist auch eine Insel
· den einschlägigen Tutorials und Dokumentationen von SUN
Aktuell stellt sich der Inhalt folgendermaßen dar:
Was ist ein Ereignis?........................................................................................................2
Von Sendern und Empfängern.........................................................................................4
Die Klasse Observable und das Interface Observer ........................................................8
GUI-Events am Beispiel eines JButtons.........................................................................11
Aufgaben.......................................................................................................................16
Übersicht über die Zuordnung der Listener-Klassen zu den einzelnen Events ..............17
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 2
Was ist ein Ereignis?
Unter einem Ereignis (javanesisch: event) wird allgemein eine spezielle Art von Nachricht
verstanden, die ein Benutzer oder ein externes Gerät (wie z.B. einer Platte, eines Modems
o.ä.) via JRE/BS an ein Java-Programm verschickt. Mausklicks, Mausbewegungen,
Tastatureingaben oder Veränderungen an der Größe oder Lage eines Fensters sind
typische Vertreter dieser Art von Nachrichten.
Die unterschiedlichen Ereignisarten sind in einer objektorientierten Programmiersprache
wie Java natürlich in Form von entsprechenden Klassen typisiert und hierarchisiert:
EventObject
AWTEvent
ActionEvent
AdjustmentEvent
AncestorEvent (Swing)
ComponentEvent
ContainerEvent
FocusEvent
InputEvent
KeyEvent
MenuKeyEvent (Swing)
MouseEvent
MenuDragMouseEvent (Swing)
MouseWheelEvent
PaintEvent
WindowEvent
HierarchyEvent
InputMethodEvent
InternalFrameEvent (Swing)
InvocationEvent
ItemEvent
TextEvent
CaretEvent (Swing)
ChangeEvent (Swing)
DocumentEvent (Swing)
HyperlinkEvent (Swing)
ListDataEvent (Swing)
ListSelectionEvent (Swing)
MenuEvent (Swing)
PopupMenuEvent (Swing)
TableColumnModelEvent (Swing)
TableModelEvent (Swing)
TreeExpansionEvent (Swing)
TreeModelEvent (Swing)
TreeSelectionEvent (Swing)
UndoableEditEvent (Swing)
Die vielfältigen Ereignistypen werden unterteilt in Low-Level-Events (alle Ereignisklassen
unterhalb von ComponentEvent, die elementare Nachrichten transferieren, die von
Fenstern, Dialogelementen oder externen Geräten stammen) und die sogenannten
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 3
semantischen Ereignisse (Ereignisse, die nicht an eine bestimmte Komponente/ein
bestimmtes Oberflächenelement/ein bestimmtes Gerät gebunden sind, sondern
abstraktere/höherwertige Nachrichten wie das Ausführen eines Kommandos oder die
Änderung eines bestimmten Systemzustands übermitteln).
Ein Ereignis kann verglichen werden mit dem Klingeln des Telefons oder dem Läuten der
Türglocke. Die Umstände sind klar: Jemand anderes hat die zugehörige Telefonnummer
gewählt bzw. die Türklingel an der Haustüre betätigt – abstrakter: Ein Ereignis erzeugt.
Der Besitzer des Telefons bzw. der Bewohner der Wohnung kann jedoch reagieren, wie er
möchte – ein fest definiertes Verhalten ist hier nicht zu erzwingen. Die Erzeugung eines
Ereignisses ist also von der Reaktion auf dieses Ereignis in signifikanter Weise entkoppelt.
Bezogen auf die oben bereits erwähnten Ereignisarten ist im Rahmen der Java-Sprach-
Spezifikation jeweils eindeutig niedergelegt, welche Umstände zur Erzeugung des
entsprechenden Ereignisses führen müssen. Festlegungen zu konkreten oder gar
verbindlichen Reaktionen sind nicht enthalten. Ereignisse (oder vielleicht sogar besser:
Ereignis-Objekte) sind also weniger durch ihr Verhalten spezifiziert als durch die
Umstände, die zu ihrer Erzeugung führen. Diese Konzeption ermöglicht es Java-
Programmierern, die Wirkung eines Ereignisses weitestgehend selbst zu definieren, also
nahezu beliebige Aktivitäten zu entfalten.
Wenn der Benutzer ein bestimmtes Ereignis auslöst, etwa indem er im Menü eines
Programms den Befehl „Speichern“ wählt, muss die Methode gestartet werden, die das
durchführt, was der Benutzer wünscht. Das Feststellen des Ereignisses und das
Ausführen der verlangten Aufgabe heißt in einem Java-Programm Ereignisbearbeitung
(javanesisch: event handling). Programme, die jederzeit auf Ereignisse reagieren können,
werden ereignisgesteuert (javanesich: event driven) genannt.
Um zu verstehen, wie die Erzeugung eines Ereignisses vor sich geht, kann man sich
zunächst mit einer vereinfachten Vorstellung behelfen: Man nehme an, dass es in Java
eine Methode gibt, die permanent in Form einer Schleife läuft und überprüft, welche
Ereignisse eingetreten sind – die sog. Ereignisschleife eben. Abhängig vom Ereignis ruft
diese Methode dann weitere Methoden auf, die auf das konkrete Ereignis spezialisiert
reagieren können.
In Wirklichkeit ist alles natürlich ein wenig komplizierter: Die Ereignisschleife läuft nicht
immer, sondern wird bei Bedarf vom Betriebssystem gestartet. Wenn der Benutzer oder
ein Gerät ein Ereignis auslöst, wird auf Hardware-Ebene ein sog. Interrupt gesetzt, der die
CPU zu bestimmten Aktionen veranlasst. CPU bzw. Betriebssystem unterbrechen dann
ohne Verzögerung das gerade laufende Programm und laden abhängig von der Art des
Interrupts eine Routine, die weitere Aktivitäten veranlasst. Sind diese Aktivitäten
komplexerer Natur (wie z.B. das Starten der Ereignisschleife in Java), wird diese Aufgabe
vorgemerkt, das gerade unterbrochene Programm zu Ende gebracht (oder zumindest im
Rahmen der eigentlich vorgesehen CPU-Zeit fortgeführt) und danach das (zur
Ereignisbearbeitung) vorgemerkte Programm ausgeführt.
Glücklicher Weise müssen sich Java-Programmierer um die meisten dieser technischen
Details nicht kümmern. Für sie reicht das im folgenden Abschnitt vorgestellte Konzept –
das sog. Delegation Event Model, das auch Delegation Based Event Handling genannt
wird..
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 4
Von Sendern und Empfängern
Für die Realisierung von ereignisgesteuerten Programmen sind drei „Zutaten“
unabdingbar: Ereigniserzeuger (Sender), Ereignisbearbeiter (Empfänger) und ein
Kommunikationsprotokoll, dass zwischen diesen beiden vermittelt.
Protokolle werden in Java zumeist in Form von Interfaces vereinbart. In diesen Interfaces
sind die Funktionalitäten spezifiziert, die im Rahmen des zu definierenden Protokolls von
einzelnen der beteiligten Objekte realisiert werden müssen. (Zur Erinnerung: Interfaces
spezifizieren Rollen, legen einen gewissen Stamm von Methoden fest, über die alle
Objekte verfügen, die aus einer Klasse stammen, die das entsprechende Interface
implementieren. Näheres dazu im Skript über Interfaces).
Der Ereigniserzeuger versendet seine Nachrichten nur an solche Objekte, die sich zuvor
als Nachrichteninteressenten bei ihm registriert haben. Eine Nachricht zu versenden heißt
in diesem Zusammenhang natürlich wieder, dass der Sender beim Empfänger eine (oder
mehrere) bestimmte Methode(n) aufrufen muss. Das wiederum setzt voraus, dass der
Empfänger die entsprechenden Methoden überhaupt zur Verfügung stellt – ein Umstand,
der nach Möglichkeit schon zur Übersetzungszeit, also vom Compiler geprüft werden
sollte und nicht erst zur Laufzeit der Applikation. Zu diesem Zweck sind die für die
Verarbeitung eines bestimmten Ereignisses notwendigen Methoden (also das
Kommunikationsprotokoll) in den oben bereits erwähnten Interfaces spezifiziert. Als
Ereignisempfänger kommen dann nur solche Objekte in Frage, die aus Klassen stammen,
die das entsprechende Interface implementieren.
Auch im Rahmen der ereignisbezogenen Kommunikationsprotokolle hat sich eine
Klassen- oder besser Interface-Hierarchie etabliert – allerdings deutlich flacher als die
korrespondierende Ereignisklassenhierarchie:
EventListener
ActionListener
AdjustmentListener
AncestorListener (Swing)
AWTEventListener
CaretListener (Swing)
CellEditorListener (Swing)
ChangeListener (Swing)
ComponentListener
ContainerListener
DocumentListener (Swing)
FocusListener
HierarchyBoundsListener
HierarchyListener
HyperlinksListener (Swing)
InternalFrameListener (Swing)
InputMethodListener
ItemListener
ListDataListener (Swing)
ListSelectionListener (Swing)
KeyListener
MenuDragMouseListener (Swing)
MenuKeyListener (Swing)
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 5
MenuListener (Swing)
MouseInputListener (Swing)
MouseListener
MouseMotionListener
MouseWheelListener
PopupMenuListener (Swing)
TableColumnModelListener (Swing)
TableModelListener (Swing)
TextListener
TreeExpansionListener (Swing)
TreeModelListener (Swing)
TreeSelectionListener (Swing)
TreeWillExpandListener (Swing)
UndoableEditListener (Swing)
WindowFocusListener
WindowListener
WindowStateListener
Für den Fall, dass in einem Interface mehr als eine ereignisbearbeitende Methode
spezifiziert wird, existiert in der Klassenbibliothek eine sogenannte Adapterklasse, also
eine Klasse, die alle Methoden eines vorgegebenen Interfaces mit leeren
Methodenrümpfen implementiert.
Was den protokollgesteuerten Nachrichtenaustausch zwischen Sender und Empfänger
angeht, so ist in diesem Zusammenhang oft vom sog. Observer-Pattern (Beobachter-
Muster) die Rede (andere Bezeichnung: Publisher-Subscriber-Pattern, von engl. to
publish: veröffentlichen, to subscribe: abonnieren, vorbestellen). Dieses Entwurfsmuster
beschreibt mit Hilfe zweier Klassen den Mechanismus „Sender kommuniziert mit
Empfänger“ oder besser „Empfänger wird vom Sender benachrichtigt“.
Da dieses Muster grundlegend für die weiter unten noch ausführlich zu diskutierende
Behandlung von GUI-Events ist, soll es hier vorgestellt werden. Zur Vorbereitung auf die
entsprechende Realisierung des Observer-Patterns mit Klassen der Klassenbibliothek
werden die Grundprinzipien des Protokolls zunächst mit einer (überschaubareren)
Eigenimplementierungen verdeutlicht.
Zu allererst werden mittels zweier Interfaces die Rollen eines Senders und eines
Empfängers beschrieben:
public interface Sender
{
public void registriereEmpfänger(Empfänger e);
public void benachrichtigeAlleEmpfänger();
}
Bei einem Sender können also potentielle Nachrichtenempfänger registriert werden.
Ferner ist ein Sender in der Lage, allen bei ihm registrierten Empfängern eine Nachricht
zukommen zu lassen.
public interface Empfänger
{
public void empfangeNachricht(Object o);
}
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 6
Ein Empfänger kann also eine (x-beliebig klassentypisierte) Nachricht empfangen.
Auf der Basis dieser grundlegenden Vereinbarungen (dieses Protokolls) kann dann eine
erste Anwendung entwickelt werden. Dazu wird ein konkreter Sender definiert:
import java.util.*;
public class Zahlenproduzent
implements Sender
{
private Vector empfänger;
private int zahl;
private Random zufzahlMacher;
public Zahlenproduzent()
{
empfänger = new Vector();
zufzahlMacher = new Random();
}
public void produziereZahl()
{
zahl = zufzahlMacher.nextInt(100);
benachrichtigeAlleEmpfänger();
}
public void registriereEmpfänger(Empfänger e)
{
empfänger.add(e);
}
public void benachrichtigeAlleEmpfänger()
{
for(int i = 0; i<empfänger.size(); i++)
{
((Empfänger)empfänger.elementAt(i)).empfangeNachricht(new Integer(zahl));
}
}
}
Und auch ein konkreter Empfänger:
public class ZahlenanzeigerGerade
implements Empfänger
{
public void gebeZahlAus(int zahl)
{
System.out.println("Eine gerade Zahl: " + zahl);
}
public void empfangeNachricht(Object o)
{
int zahl = ((Integer)o).intValue();
if(zahl%2==0)
this.gebeZahlAus(zahl);
}
}
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 7
Zusammen mit der Start-Klasse
public class Start
{
public static void main(String[] args)
{
Zahlenproduzent zp = new Zahlenproduzent();
ZahlenanzeigerGerade zag = new ZahlenanzeigerGerade();
zp.registriereEmpfänger(zag);
for(int i = 0; i<10; i++)
{
zp.produziereZahl();
System.out.println("***Ende " + i + "ter Durchgang***");
}
}
}
entsteht z.B. die folgende Konsolen-Ausgabe:
***Ende 0ter Durchgang***
***Ende 1ter Durchgang***
Eine gerade Zahl: 88
***Ende 2ter Durchgang***
***Ende 3ter Durchgang***
Eine gerade Zahl: 50
***Ende 4ter Durchgang***
***Ende 5ter Durchgang***
Eine gerade Zahl: 88
***Ende 6ter Durchgang***
***Ende 7ter Durchgang***
***Ende 8ter Durchgang***
***Ende 9ter Durchgang***
Das Zusammenspiel der Klassen wird durch das folgende Klassendiagramm verdeutlicht:
Zahlenproduzent
ZahlenanzeigerGerade
<<interface>>
Sender
<<interface>>
Empfänger
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 8
Die Klasse Observable und das Interface Observer
Wie bereits erwähnt, existieren für die Umsetzung des Observer-Patterns in der Java-
Klassenbibliothek bereits entsprechende Vorgaben. Im Gegensatz zu der oben
vorgestellten Eigen-Implementierung wird allerdings nur die Empfängerrolle in Form eines
Interfaces spezifiziert (java.util.Observer), für das sendende Objekt existiert eine
Basisklasse (java.util.Observable), in der insbesondere die Registrierung bereits
komfortabel geregelt ist und in der die Umstände, die zur Erzeugung und Versendung
einer Nachricht führen müssen, eindeutig festgelegt sind.
Die wichtigsten Methoden der Klasse Observable:
Methode Beschreibung
public Observable() erzeugt ein neues Observable, bei dem
noch kein Observer registriert ist
public void addObserver(Observer o) fügt der Menge der bisher registrierten
Observer den in der Parameterliste
angegebenen hinzu
public void deleteObserver(Observer o) löscht den angegebenen Observer aus
der Liste der bisher registrierten Observer
protected void setChanged() setzt ein internes boolesches Flag, aus
dem hervorgeht, dass sich der Zustand
des Objekts geändert hat
protected void clearChanged() setzt das boolesche Flag wieder zurück;
diese Methode wird automatisch am Ende
von notifyObservers() aufgerufen
public boolean hasChanged() gibt den Wert des booleschen Flags
zurück
public void notifyObservers(Object obj) falls das boolesche Flag auf true steht,
wird bei allen registrierten Observern die
update()-Methode aufgerufen, dabei kann
als zusätzliche Information ein Objekt vom
Klassentyp Object mitgegeben werden;
die Methode endet stets mit dem internen
Aufruf von clearChanged()
public void notifyObservers() wie oben, nur ohne die Möglichkeit,
zusätzliche Informationen mit zu
versenden
Die Methode des Interfaces Observer
Methode Beschreibung
public void update(Observable obs, Object
obj)
wird vom Observable nach jeder
Zustandsänderung aufgerufen; die
Methode bekommt (von der
Laufzeitumgebung) eine Referenz auf
das aufrufende Observable übergeben;
der Inhalt des zweiten Parameters ist
abhängig davon, welche der
notifyObservers()-Methoden aufgerufen
worden ist
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 9
Hier zunächst wieder ein einfaches Beispiel zur Veranschaulichung. Es besteht aus zwei
Klassen, eine, die Daten verändern kann und eine andere, die auf geänderte Daten
reagiert:
import java.util.*;
public class TemperaturGeber extends Observable
{
private int temp = 30;
public void tempVerändern()
{
double d;
d = Math.random();
if(d>0.75)
{
temp++;
setChanged();
}
else if (d<0.25)
{
temp--;
setChanged();
}
System.out.println("Gesetzte Temperatur: " + temp);
notifyObservers();
}
public int getTemp()
{
return temp;
}
}
import java.util.Observable;
import java.util.Observer;
public class TemperaturMesser implements Observer
{
public void update(Observable obs, Object obj)
{
System.out.println("Gemessene Temperatur: " +
(TemperaturGeber)obs).getTemp());
}
public static void main (String [] args)
{
TemperaturGeber tg = new TemperaturGeber();
TemperaturMesser tm = new TemperaturMesser();
tg.addObserver(tm);
for(int i = 0; i < 50; i++)
{
tg.tempVerändern();
System.out.println("***Ende " + i + "ter Durchgang***");
}
}
}
Das zugehörige Klassendiagramm:
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 10
Und die Konsolenausgabe:
Gesetzte Temperatur: 30
***Ende 0ter Durchgang***
Gemessene Temperatur: 29
Gesetzte Temperatur: 29
***Ende 1ter Durchgang***
Gemessene Temperatur: 30
Gesetzte Temperatur: 30
***Ende 2ter Durchgang***
Gesetzte Temperatur: 30
***Ende 3ter Durchgang***
Gemessene Temperatur: 31
Gesetzte Temperatur: 31
***Ende 4ter Durchgang***
Gemessene Temperatur: 32
Gesetzte Temperatur: 32
***Ende 5ter Durchgang***
Gemessene Temperatur: 33
Gesetzte Temperatur: 33
***Ende 6ter Durchgang***
Gemessene Temperatur: 34
Gesetzte Temperatur: 34
***Ende 7ter Durchgang***
Gemessene Temperatur: 33
Gesetzte Temperatur: 33
***Ende 8ter Durchgang***
Gemessene Temperatur: 32
Gesetzte Temperatur: 32
***Ende 9ter Durchgang***
TemperaturGeber
Observable
TemperaturMesser
<<interface>>
Observer
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 11
GUI-Events am Beispiel eines JButtons
Ereignisse, die von Komponenten ausgelöst werden, die dem Benutzer über eine
graphische Oberfläche zugänglich gemacht werden, heißen in der Java-Welt auch GUIEvents
(GUI – graphical user interface). Wie oben bereits ausführlich erläutert, werden
diese Ereignisse in Form von Nachrichten von ihrem Erzeuger, der Ereignisquelle, an
einen oder mehrere Bearbeiter gesendet. Als Bearbeiter kommen nur solche Objekte in
Frage, die einer Klasse entstammen, die das für diese Art von Ereignisnachrichten
adäquate Interface implementiert.
Das prinzipielle Prozedere bei der Implementierung ähnelt der oben schon vorgestellten
Vorgehensweise bei der Umsetzung des Observerpatterns. Der Ereigniserzeuger nimmt
die Rolle des Observables ein, bei dem sich Observer registrieren können, die dann nach
einer entsprechenden Zustandsänderung benachrichtigt werden. Als Kochrezept:
1. Erzeugung einer passenden Ereignisquelle.
2. Erzeugung des zu dieser Ereignisart passenden Ereignisbearbeiters.
3. Registrierung des Ereignisbearbeiters beim Ereigniserzeuger.
Der konzeptionell aufwändigste Schritt ist dabei die Erzeugung des Ereignisempfängers.
Aus welcher Klasse soll er stammen?
a) Aus der Klasse, die auch den Ereigniserzeuger zur Verfügung stellt?
Vorteil: Recht einfache Implementierung.
Nachteil: Es entsteht eine schlecht wartbare und häufig auch unübersichtliche All-
Inclusive-Klasse, da u.U. viele leere Methodenrümpfe enthalten sind.
b) Aus einer selbstständigen externen Klasse?
Vorteil: Absolut lupenreine Kapselung.
Nachteil: Erhöhter Kommunikationsaufwand, da alle vom Ereignisbearbeiter
benötigten Referenzen übergeben werden müssen.
c) Aus einer in der Klasse, die auch den Ereigniserzeuger zur Verfügung stellt,
definierten lokalen/inneren Klasse?
Vorteil: Recht einfache, dem Grundgedanken der Kapselung verpflichtete
Implementierung, die den erhöhten Kommunikationsaufwand von Variante b)
vermeidet. (In der Dokumentation zur J2SE wird diese Vorgehensweise für die
Ereignisbearbeitung in kleinen Programmen oder bei Komponenten mit einer
einfachen Kommunikationsstruktur ausdrücklich empfohlen.)
Nachteil: Für größere Anwendungen, die eine strikte Trennung zwischen
Programmcode für die Oberfläche und Programmcode für die Anwendungslogik
verlangen, ist diese Art der Implementierung nicht geeignet. Auch ist diese Lösung
von der objektorientierten Programmiertechnik her in einigen Zusammenhängen
anspruchsvoll. Für ausführliche Erläuterungen zum Konzept der lokalen Klassen sei
auf das Kapitel 10.1 im G. Krüger verwiesen.
Die vierte denkbare Version – die Verwendung von anonymen inneren Klassen – wird hier
bewusst nicht thematisiert, da sie einerseits ein tiefergehendes Grundverständnis der dazu
notwendigen (und teilweise recht abenteuerlich anmutenden) Syntax- und
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 12
Programmstrukturen voraussetzt (ebenfalls nachzulesen im Kapitel 10.1 von G. Krüger)
und andererseits aus designtechnischen Gesichtspunkten ohnehin nicht zu empfehlen ist.
Für jede der oben vorgestellten Varianten wird nachfolgend je ein Quelltextbeispiel
angegeben. Es handelt sich jeweils um die Implementierung einer Applikation, bei der
durch die Aktivierung einer Schaltfläche die Hintergrundfarbe des Fensters geändert wird
– von gelb auf grün und umgekehrt.
Variante a)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VarianteA
extends JFrame
implements ActionListener
{
public VarianteA()
{
this.setSize(400,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.erzeugeAnsicht();
this.show();
}
private void erzeugeAnsicht()
{
JPanel pan = new JPanel();
JButton knopf = new JButton("Klick mich!");
knopf.addActionListener(this);
pan.add(knopf);
this.getContentPane().setBackground(Color.green);
this.getContentPane().add(pan,"North");
}
public void actionPerformed(ActionEvent arg0)
{
if(this.getContentPane().getBackground()==Color.green)
this.getContentPane().setBackground(Color.yellow);
else
this.getContentPane().setBackground(Color.green);
this.repaint();
}
public static void main(String[] args)
{
new VarianteA();
}
}
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 13
Variante b)
import java.awt.Color;
import javax.swing.*;
public class FensterVonVarianteB extends JFrame
{
public FensterVonVarianteB()
{
this.setSize(400,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.erzeugeAnsicht();
this.show();
}
private void erzeugeAnsicht()
{
JPanel pan = new JPanel();
JButton knopf = new JButton("Klick mich!");
ExternerEreignisbearbeiter eeb = new ExternerEreignisbearbeiter(this);
knopf.addActionListener(eeb);
pan.add(knopf);
this.getContentPane().setBackground(Color.green);
this.getContentPane().add(pan,"North");
}
public static void main(String[] args)
{
new FensterVonVarianteB();
}
}
import java.awt.Color;
import java.awt.event.*;
import javax.swing.JFrame;
public class ExternerEreignisbearbeiter implements ActionListener
{
private JFrame anwendungsfenster;
public ExternerEreignisbearbeiter(FensterVonVarianteB fenster)
{
anwendungsfenster = fenster;
}
public void actionPerformed(ActionEvent arg0)
{
if(anwendungsfenster.getContentPane().getBackground()==Color.green)
anwendungsfenster.getContentPane().setBackground(Color.yellow);
else
anwendungsfenster.getContentPane().setBackground(Color.green);
}
}
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 14
Variante c)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VarianteC extends JFrame
{
public VarianteC()
{
this.setSize(400,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.erzeugeAnsicht();
this.show();
}
private void erzeugeAnsicht()
{
JPanel pan = new JPanel();
JButton knopf = new JButton("Klick mich!");
InnererEreignisbearbeiter ieb = new InnererEreignisbearbeiter();
knopf.addActionListener(ieb);
pan.add(knopf);
this.getContentPane().setBackground(Color.green);
this.getContentPane().add(pan,"North");
}
public static void main(String[] args)
{
new VarianteC();
}
class InnererEreignisbearbeiter
implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(VarianteC.this.getContentPane().getBackground()==Color.green)
VarianteC.this.getContentPane().setBackground(Color.yellow);
else
VarianteC.this.getContentPane().setBackground(Color.green);
}
}
}
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 15
Und als mahnendes Beispiel auch noch Variante d)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VarianteD extends JFrame
{
public VarianteD()
{
this.setSize(400,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.erzeugeAnsicht();
this.show();
}
private void erzeugeAnsicht()
{
JPanel pan = new JPanel();
JButton knopf = new JButton("Klick mich!");
knopf.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e)
{
if(VarianteD.this.getContentPane().getBackground()==Color.green)
VarianteD.this.getContentPane().setBackground(Color.yellow);
else
VarianteD.this.getContentPane().setBackground(Color.green);
}
}
);
pan.add(knopf);
this.getContentPane().setBackground(Color.green);
this.getContentPane().add(pan,"North");
}
public static void main(String[] args)
{
new VarianteD();
}
}
Alternativ wird auch noch über die Überlagerung der Komponenteneigenen Event-Handler,
also den expliziten Eingriff in die Standardimplementierung des Nachrichtenverkehrs
gesprochen (vgl. G. Krüger, 28.2, Entwurfsmuster für den Nachrichtenverkehr) oder auch
über den noch nicht so weit verbreiteten Einsatz sogenannter generischer
Ereignisbearbeiter (C. Ullenboom, Kap. 15.3.4). Beide Varianten sind aber eher für ganz
spezielle Implementierungsproblematiken gedacht und werden hier deshalb nicht näher
thematisiert.
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 16
Aufgaben
1. Ergänzen Sie das Beispiel aus dem Kapitel „Von Sendern und Empfängern“ um
eine weitere Empfänger-Klasse: Die Klasse ZahlanzeigerUngerade soll eine Zahl
nur dann ausgeben, wenn sie ungerade ist. Registrieren Sie dann zunächst nur ein
ZahlanzeigerUngerade-Objekt als Empfänger beim Zahlenproduzenten. Wie verhält
sich die Applikation, wenn beide Zahlanzeiger als Empfänger registriert sind?
2. Setzen Sie das Beispiel mit den Klassen Zahlproduzent und ZahlAnzeigerGerade
noch einmal um – diesmal mit Hilfe der Klassen Observable und Observer.
3. Bringen Sie alle in diesem Skript abgedruckten Quelltexte zum Laufen und
kommentieren Sie sie geeignet aus.
Java Ereignisgesteuerte Programmierung
Dipl.-Math. Eckard Fischer 17
Übersicht über die Zuordnung der Listener-Klassen zu den
einzelnen Events
AWTEvent
ActionEvent
AdjustmentEvent
AncestorEvent (Swing)
ComponentEvent
ContainerEvent
FocusEvent
InputEvent
KeyEvent
MenuKeyEvent (Swing)
MouseEvent
MenuDragMouseEvent (Swing)
MouseWheelEvent
PaintEvent
WindowEvent
HierarchyEvent
InputMethodEvent
InternalFrameEvent (Swing)
InvocationEvent
ItemEvent
TextEvent
CaretEvent (Swing)
ChangeEvent (Swing)
DocumentEvent (Swing)
HyperlinkEvent (Swing)
ListDataEvent (Swing)
ListSelectionEvent (Swing)
MenuEvent (Swing)
PopupMenuEvent (Swing)
TableColumnModelEvent (Swing)
TableModelEvent (Swing)
TreeExpansionEvent (Swing)
TreeModelEvent (Swing)
TreeSelectionEvent (Swing)
UndoableEditEvent (Swing)
AWTEventListener
ActionListener
AdjustmentListener
AncestorListener (Swing)
ComponentListener
ContainerListener
FocusListener
KeyListener
MenuKeyListener (Swing)
MouseListener
MouseMotionListener
MouseInputListener (Swing)
MenuDragMouseListener (Swing)
MouseWheelListener
WindowFocusListener
WindowListener
WindowStateListener
HierarchyBoundsListener
HierarchyListener
InputMethodListener
InternalFrameListener (Swing)
AWTEventListener
ItemListener
TextListener
CaretListener (Swing)
ChangeListener (Swing)
DocumentListener (Swing)
HyperlinksListener (Swing)
ListDataListener (Swing)
ListSelectionListener (Swing)
MenuListener (Swing)
PopupMenuListener (Swing)
TableColumnModelListener (Swing)
TableModelListener (Swing)
TreeExpansionListener (Swing)
TreeModelListener (Swing)
TreeSelectionListener (Swing)
TreeWillExpandListener (Swing)
UndoableEditListener (Swing)
Keine Kommentare:
Kommentar veröffentlichen