Kapitel 7: Ereignis-basierte Kommunikation

Werbung
Liste P: Programmieren mit Java
WS 2001/2002
Prof. Dr. V. Turau
FH Wiesbaden
Kapitel 7: Ereignis-basierte Kommunikation
Folie 157 : Beispiel: Die Klasse MouseEvent
Wichtige Methoden der Klasse MouseEvent:
Methode
public int getClickCount()
Bedeutung
Anzahl der Mausclicks für dieses Ereignis
public int getX()
x-Koordinate des Ereignisses
publicint getY()
y-Koordinate des Ereignisses
public long getWhen()
public boolean isAltDown()
public boolean isShiftDown()
public Component getComponent()
Zeitstempel
Wurde die Alt-Taste beim Klicken gedrückt
Wurde die Shift-Taste beim Klicken gedrückt
Ereignisquelle
Folie 158 : Demo: MouseEvents
public class MouseEventDemo extends JFrame
implements MouseListener {
public void init() {
Feld feld = new Feld(new Color(0.98f, 0.97f, 0.85f));
contentPane.add(feld);
feld.addMouseListener(this);
addMouseListener(this);
}
public void mousePressed(MouseEvent e) {
ausgabe("Mouse pressed (Anzahl clicks: " + e.getClickCount()
+ ", (" + e.getX() + ", " + e.getY() + "))", e);
}
public void mouseReleased(MouseEvent e) {
ausgabe("Mouse released (Anzahl clicks: " + e.getClickCount()
+ ", (" + e.getX() + ", " + e.getY() + "))", e);
}
Folie 159 : Demo: MouseEvents (Forts.)
public void mouseEntered(MouseEvent e) {
ausgabe("Mouse entered", e);
}
public void mouseExited(MouseEvent e) {
ausgabe("Mouse exited", e);
}
public void mouseClicked(MouseEvent e) {
ausgabe("Mouse clicked (Anzahl clicks: "
+ e.getClickCount() + ")", e);
}
void ausgabe(String beschreibung, MouseEvent e) {
System.out.println(beschreibung + " festgestellt in "
+ e.getComponent().getClass().getName());
}
}
Folie 160 : Demo: MouseEvents (Ausgabe)
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
Mouse
entered festgestellt in MouseEventDemo
entered festgestellt in Feld
exited festgestellt in Feld
pressed (Anzahl clicks: 1, (303, 261)) festgestellt in MouseEventDemo
released (Anzahl clicks: 1, (193, 289)) festgestellt in MouseEventDemo
entered festgestellt in Feld
pressed (Anzahl clicks: 1, (194, 131)) festgestellt in Feld
released (Anzahl clicks: 1, (194, 131)) festgestellt in Feld
clicked (Anzahl clicks: 1) festgestellt in Feld
pressed (Anzahl clicks: 2, (194, 131)) festgestellt in Feld
released (Anzahl clicks: 2, (194, 131)) festgestellt in Feld
clicked (Anzahl clicks: 2) festgestellt in Feld
exited festgestellt in Feld
exited festgestellt in MouseEventDemo
Folie 161 : Ereignisempfänger
In vielen Fällen ist man nicht an allen Ereignissen einer Kategorie interessiert (z.B. nur an
mouseClicked)
Trotzdem muss man alle Methoden implementieren
Abhilfe: Adapterklassen
Sie implementieren alle Methoden eines Interfaces (jeweils keine Aktionen)
Werden Ereignisempfänger als Unterklassen solcher Adapterklassen realisiert, so muss man nur
noch die gewünschten Methoden überschreiben
Adapterklassen sind nur für Listener-Interfaces mit mehreren Methoden interessant (Warum?)
Folie 162 : Adapterklassen
Beispiel: MouseAdapter
public 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) {}
}
Anwendung: MouseEventDemo (nur mouseClicked-Events)
public class MouseEventDemo extends JFrame, MouseAdapter {
..
Problem: MouseEventDemo kann nur eine Oberklasse haben
Folie 163 : Adapterklassen
Lösung: Getrennte Ereignisempfängerklasse
public class MouseEventHandling extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked (Anzahl clicks: "
+ e.getClickCount() + ", (" + e.getX() + ", " + e.getY() + "))"
+ e.getComponent().getClass().getName());
}
}
Verwendung in MouseEventDemo:
public void init() {
Feld feld = new Feld(new Color(0.98f, 0.97f, 0.85f));
contentPane.add(feld);
MouseEventHandling meh = new MouseEventHandling();
feld.addMouseListener(meh);
addMouseListener(meh);
}
Folie 164 : Getrennte Ereignisempfängerklassen
Vorteile:
Bessere Trennung von Struktur und Funktion
Verwendung von Adapterklassen ist möglich
Nachteile:
Ereignisempfängerklasse hat keinen Zugriff auf die Komponenten der Anwendung
Folie 165 : Getrennte Ereignisempfängerklassen (Beispiel)
Beispiel: KommunikationsFrame mit Button zum Löschen
public class KommunikationsFrame extends JFrame
implements ActionListener {
JTextField f1, f2;
private Container contentPane;
private final String RESET = "Löschen";
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(RESET)) {
f1.setText("");
f2.setText("");
}
else
f2.setText(f1.getText().toUpperCase());
}
Ereignisempfängerklasse braucht Zugriff auf f1, f2
Folie 166 : Getrennte Ereignisempfängerklassen (Beispiel)
Lösung: Private Instanzvariablen
public class KommunikationsHandler implements ActionListener {
JTextField f1, f2;
public KommunikationsHandler(JTextField f1, JTextField f2) {
this.f1 = f1;
this.f2 = f2;
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(RESET)) {
f1.setText("");
f2.setText("");
}
else
f2.setText(f1.getText().toUpperCase());
}
Folie 167 : Getrennte Ereignisempfängerklassen (Verwendung)
public void init() {
action = new JButton("Mache zu Großbuchstaben");
contentPane.add(action);
JButton reset = new JButton("Löschen");
contentPane.add(reset);
JButton b = new JButton("Mache zu Großbuchstaben");
contentPane.add(b);
f1 = new JTextField(30);
contentPane.add(f1);
f2 = new JTextField(30);
f2.setEditable(false);
contentPane.add(f2);
KommunikationsHandler kh = new KommunikationsHandler(f1, f2);
action.addActionListener(kh);
reset.addActionListener(kh);
}
Folie 168 : Ereignisempfängerklassen
Benötigen Ereignisempfängerklassen den Zugriff auf Instanzvariablen, so ist der Umgang mit
getrennten Ereignisempfängerklassen oft etwas umständlich
Abhilfe:
Innere Klassen
Anonyme innere Klassen
Diese Konzepte wurden in Java 1.1 erstmals eingeführt
Folie 169 : Innere Klassen
Innere Klassen sind Klassen in Klassen
Struktur:
class Klasse {
. . .
class InnereKlasse {
. . .
}
}
Verwendung: Die innere Klasse hat nur Bedeutung innerhalb der Klasse
Innere Klassen haben direkten Zugriff auf Instanzvariablen und Instanzmethoden der umgebenden
Klasse
Folie 170 : Innere Klassen (Anwendung)
Anwendung mit zwei Buttons (Wandle in Großbuchstaben und Reset
public class KommunikationsFrame extends JFrame {
JTextField f1, f2;
.....
class KommunikationsHandlerReset implements ActionListener {
public void actionPerformed(ActionEvent e) {
f1.setText("");
f2.setText("");
}
}
class KommunikationsHandlerDo implements ActionListener {
public void actionPerformed(ActionEvent e) {
f2.setText(f1.getText().toUpperCase());
}
}
Folie 171 : Innere Klassen (Anwendung)
public void init() {
JButton b = new JButton("Mache zu Großbuchstaben");
contentPane.add(b);
b.addActionListener(new KommunikationsHandlerDo());
JButton reset = new JButton(RESET);
contentPane.add(reset);
reset.addActionListener(new KommunikationsHandlerReset());
f1 = new JTextField(30);
contentPane.add(f1);
f2 = new JTextField(30);
f2.setEditable(false);
contentPane.add(f2);
}
}
Vorteil: Code zur Ereignisbehandlung an einer Stelle konzentriert
Folie 172 : Anonyme Klassen
Anonyme Klassen sind innere Klassen ohne Namen
Anonyme Klassen
implementieren ein Interface oder
sind Unterklassen einer existierenden Klasse
Anonyme Klassen erschweren die Lesbarkeit des Codes und sollten nur für sehr kurze
Klassenvereinbarungen verwendet werden
Hauptanwednung: Ereignisbehandlung
Folie 173 : Anonyme Klassen (Beispiel 1)
Anwendung mit zwei Buttons (Wandle in Großbuchstaben und Reset
public class KommunikationsFrame extends JFrame {
JTextField f1, f2;
....
public void init() {
JButton b = new JButton("Mache zu Großbuchstaben");
contentPane.add(b);
b.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
f2.setText(f1.getText().toUpperCase());
}
}
);
JButton reset = new JButton(RESET);
contentPane.add(reset);
reset.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
f1.setText("");
f2.setText("");
}
}
);
...
Folie 174 : Anonyme Klassen (Beispiel 2)
Anwendung Anwendung beenden, wenn entsprechendes Icon gedrückt wird
Hierzu muss das Interface WindowListener implementiert werden (7 Methoden)
In den meisten Fällen benötigt man nur die Methode windowClosing(WindowEvent e)
In diesem Fall bietet sich die Verwendung der Adapterklasse WindowAdapter an
Folie 175 : Anonyme Klassen (Beispiel 2)
public class KommunikationsFrame extends JFrame {
...
public KommunikationsFrame5 () {
contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.setBackground(Color.black);
setTitle("Kommunikation");
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
);
}
...
Folie 176 : Ereignisempfängerklassen
Möglichkeiten zur Implementierung von Ereignisempfängerklassen
Eine Anwendungsklasse verarbeitet alle auftretenden Ereignissee selber (Hierzu muss sie die
entsprechenden Listener-Interfaces implementieren)
Eine seperate Klasse verarbeitet alle auftretenden Ereignisse (Hierzu muss sie und nicht die
Anwendungsklasse die entsprechenden Listener-Interfaces implementieren)
Die Implementierung kann durch die Verwendung von Adapterklassen und inneren/anonymen
Klassen vereinfacht werden
Folie 177 : Animationen
Zur Erstellung von Animationen benötigt man eine periodische Ereignisquelle
Die Klasse Timer kann dazu verwendet werden, in vorgegebenen Zeitabständen ein ActionEvent
auszulösen
Konstruktor der Klasse Timer
public Timer(int verzögerung, ActionListener listener)
Nach jeweils verzögerung Millisekunden wird ein ActionEvent generiert und listener wird
benachrichtigt
Zusätzlicher Listener können mit addActionListener registriert werden
Timer können mit den Methoden start und stop jederzeit gestartet bzw. angehaltem werden
Folie 178 : Beispiel
Eine Folge von Bildern soll hintereinander angezeit werden
Hierdurch entsteht der Eindruck der Bewegung
Ein Timer steuert den Bildwechsel
Die Bildfrequenz kann über einen Slider gesteuert werden
Ein Slider erlaubt die Einstellung einer nummerischen Größe, nach Veränderungen wird ein
ChangeEvent ausgelöst
Wird die Anwendung zu einem Icon verkleinert, so soll die Animation angehalten werden
Folie 179 : Sliderdemo
Folie 180 : Sliderdemo
Beteiligte Komponenten:
Timer: löst ActionEvents aus
Slider: löst ChangeEvents aus
JFrame: löst WindowEvents aus
Implementierung der Listener:
ActionListener durch die Klasse selber
ChangeListener als innere Klasse
WindowListener als anonyme Klasse
Folie 181 : Sliderdemo (Erzeugung der Komponenten)
public class SliderDemo extends JFrame implements ActionListener {
static final int FPS_INIT = 10;
int frameNumber = 0;
int delay = 1000 / FPS_INIT;
Timer timer;
boolean frozen = false;
JLabel picture;
private JPanel contentPane;
public SliderDemo(String title) {
contentPane = (JPanel)getContentPane();
setTitle(title);
JLabel sliderLabel =
new JLabel("Frames Pro Sekunde", JLabel.CENTER);
sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
JSlider framesPerSecond =
new JSlider(JSlider.HORIZONTAL, 0, 50, FPS_INIT);
// Markierungen des Sliders
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setMinorTickSpacing(1);
framesPerSecond.setPaintTicks(true);
framesPerSecond.setPaintLabels(true);
Folie 182 : Sliderdemo (Erzeugung der Komponenten)
picture = new JLabel(new ImageIcon(
"images/T" + frameNumber + ".gif"), JLabel.CENTER);
picture.setAlignmentX(Component.CENTER_ALIGNMENT);
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
contentPane.add(sliderLabel);
contentPane.add(framesPerSecond);
contentPane.add(picture);
contentPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
timer = new Timer(delay, this);
timer.setInitialDelay(delay * 20);
timer.setCoalesce(true);
Folie 183 : Sliderdemo (Verarbeitung der Window-Events)
addWindowListener(
new WindowAdapter() {
public void windowIconified(WindowEvent e) {
stopAnimation();
}
public void windowDeiconified(WindowEvent e) {
startAnimation();
}
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
);
}// Ende des Konstruktors
Folie 184 : Sliderdemo (Innere Klasse für Slider-Änderungen)
class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
//Wenn der Benutzer gerade den Wert verändert machen wir nichts
if (!source.getValueIsAdjusting()) {
int fps = (int)source.getValue();
if (fps == 0) {
if (!frozen)
stopAnimation();
} else {
delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 20);
if (frozen)
startAnimation();
}
}
}
}
Folie 185 : Sliderdemo (Verarbeitung der Timer-Events)
public void actionPerformed(ActionEvent e) {
if (frameNumber==13) {
frameNumber = 0;
} else {
frameNumber++;
}
picture.setIcon(new ImageIcon("images/T" + frameNumber + ".gif"));
}
public void startAnimation() {
timer.start();
frozen = false;
}
public void stopAnimation() {
timer.stop();
frozen = true;
}
Folie 186 : Sliderdemo (Hauptprogramm)
public static void main(String[] args) {
SliderDemo animator = new SliderDemo("SliderDemo");
animator.pack();
animator.setVisible(true);
animator.startAnimation();
}
Programm über Web-Seite verfügbar
Herunterladen