Logo Logo
InfoHome Themen Projekte Links Software
Themen
JavaHamster
BlueJ
Java
Sprachelemente
Abstrakte Datentypen
Swing
Composite Pattern
AWT > Swing
GUI-Grundstruktur
Farben und Fonts
Layout-Manager
Komponenten 1
Komponenten 2
Komponenten 3
Container
Observer Pattern
Ereignisverarbeitung
MVC-Pattern
Game Of Life
Threads
Aufgaben
Sortieren
HTML
XHTML
CSS
XML
Datenbanken
MySQL
Theoretische Informatik
PHP
Kara
Lego-Roboter
Algorithmen

Ereignisverarbeitung

Bisher sehen unsere grafischen Oberflächen zwar schön aus, sind aber noch ohne praktischen Nutzen, da wir noch keine Ereignisverarbeitung realisiert haben.

Der Mechanismus der Ereignisverarbeitung in Java lässt sich sehr gut mit entsprechenden Vorgängen in der realen Welt vergleichen. Nehmen wir zum Beispiel eine Trommel als Quelle eines Ereignisses, so können wir durch einen Schlag auf die Trommel mit dem Schlägel ein Ereignis auslösen, das von einem Empfänger (einem Zuhörer) vernommen werden kann. In Java versteht man unter einem Ereignis (event) etwas, das die Benutzerin bzw. der Benutzer beim Arbeiten mit der grafischen Oberfläche auslösen kann, indem sie bzw. er beispielsweise die Maus bewegt, eine Schaltfläche drückt, einen Menü-Eintrag auswählt oder eine Taste betätigt. Innerhalb eines Java-Programms werden solche Ereignisse als Objekte von Ereignis-Klassen dargestellt. Erzeugt werden diese Objekte von den so genannten Ereignisquellen (event sources), die entsprechende Ereignisse auslösen können. Jede Komponente einer grafischen Oberfläche (z.B. ein Button, auf den gerade gedrückt wird) kann eine solche Ereignisquelle sein.

Die Gegenstücke zu den Ereignisquellen bilden die so genannten Ereignisempfänger (event listener). Dabei handelt es sich um Objekte, die quasi als "Aufpasser" oder "Lauscher" dienen können. Damit ein solches Empfänger-Objekt auch tatsächlich Ereignisse von einer Ereignisquelle empfangen kann, muss es nach seiner Erzeugung bei der entsprechenden Ereignisquelle registriert werden.

Einfaches Beispiel: Eine Ereignisquelle

In diesem Beispiel sezten wir das Konzept der Ereignisverarbeitung in Java an einem sehr einfachen Beispiel um: Die grafische Oberfläche besteht nur aus einem Button. Ein Klick auf diesen Button soll die Hintergrundfarbe unseres Containers ändern. Bevor wir die Ereignisverarbeitung einbauen, sieht unsere GUI-Klasse folgendermaßen aus:

import javax.swing.*;
import java.awt.*;

public class Farbwechsel extends JFrame {
Container c;
JButton button;

public Farbwechsel(){
// Container bestimmen
c = getContentPane();
// Button erzeugen und dem Container hinzufügen
button = new JButton("Hintergrundfarbe wechseln");
c.add(button, BorderLayout.NORTH);
}

public static void main(String[] args){
Farbwechsel fenster = new Farbwechsel();
fenster.setTitle("Farbwechsel");
fenster.setSize(200,100);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Zunächst kümmern wir uns um unseren Ereignisempfänger (Listener-Objekt). Dazu müssen wir zunächst wissen, auf welche Art von Event unser Listener ansprechen soll. Im Falle eines gedrückten Buttons handelt es sich um ein Action-Event (ein Objekt der Klasse ActionEvent). Nun gilt es also eine Klasse zu schreiben, deren Objekte wissen, was beim Empfang eines Action-Ereignisses zu tun ist.

Um dies zu gewährleisten, müssen wir uns beim Programmieren an bestimmte Regeln halten, die in einem Interface festgelegt sind. Für Action-Ereignisse zuständig ist das Interface ActionListener, das somit von unserer Listener-Klasse implementiert werden muss. Das Interface ActionListener zwingt uns die Methode actionPerformed zu implementieren. Diese Methode wird aufgerufen, sobald unser Listener-Objekt ein Action-Event empfängt.

class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
// Hintergrundfarbe des Containers zufällig ändern
float zufall = (float) Math.random();
Color grauton = new Color(zufall, zufall, zufall);
c.setBackground(grauton);
}
}

In der Methode actionPerformed sorgen wir dafür, dass sich die Hintergrundfarbe des Containers ändert. Realisiert man die Klasse ButtonListener nun als eigene Klasse hat man nun das Problem, dass man auf die Container-Variable c keinen Zugriff hat. Eine Lösungsmöglichkeit besteht darin, die Klasse ButtonListener als innere Klasse innerhalb der Klasse Farbwechsel zu implementieren.

Unter Verwendung der Klasse ButtonListener sind wir nun in der Lage, mit

ButtonListener bL = new ButtonListener();
button.addActionListener(bL);

das Empfänger-Objekt zu erzeugen und bei der Eventquelle zu registrieren. Die dabei eingesetzte Methode addActionListener wird in der Klasse JButton bereitgestellt und erwartet einen Parameter vom Typ ActionListener, d.h. ein Objekt einer Klasse, die das Action-Listener-Interface implementiert hat. Insgesamt hat das Programm folgende Gestalt:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Farbwechsel extends JFrame {
Container c;
JButton button;

public Farbwechsel(){
// Container bestimmen
c = getContentPane();
// Button erzeugen und dem Container hinzufügen
button = new JButton("Hintergrundfarbe wechseln");
c.add(button, BorderLayout.NORTH);

// Listener-Objekt erzeugen und beim Button registrieren
ButtonListener bL = new ButtonListener();
button.addActionListener(bL);
}

// Innere Button-Listener Klasse
class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
float zufall = (float) Math.random();
Color grauton = new Color(zufall, zufall, zufall);
c.setBackground(grauton); // Zugriff auf c möglich
}
}

public static void main(String[] args){
Farbwechsel fenster = new Farbwechsel();
fenster.setTitle("Farbwechsel");
fenster.setSize(200,100);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Zweites Beispiel: Mehrere Ereignisquellen

Sobald die grafische Oberfläche aus mehreren Komponenten besteht, die in der Lage sind, dieselben Events zu erzeugen, stellt sich die Frage, wie man die Ereignisquelle eines Events identifizieren kann. Im Falle von ActionEvents lassen sich dafür die Methoden addActionCommand und getActionCommand verwenden.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Bilderrahmen extends JFrame {
Container c;
JMenuBar menuBar;
JMenu menu;
JMenuItem menuItem1, menuItem2, menuItem3;
JButton button1, button2, button3;
JToolBar toolBar;
JLabel bildLabel;

public Bilderrahmen(){
c = getContentPane();

//Erzeuge das Listener-Objekt für das Menü
MenuListener mL = new MenuListener();

// Erzeuge die Menüleiste
menuBar = new JMenuBar();
// Erzeuge ein Menü
menu = new JMenu("Bilder");

// Erzeuge die Menüeinträge und füge sie dem menü hinzu
menuItem1 = new JMenuItem("Music");
menuItem1.addActionListener(mL);
menu.add(menuItem1);
menuItem2 = new JMenuItem("Like a Virgin");
menuItem2.addActionListener(mL);
menu.add(menuItem2);
menuItem3 = new JMenuItem("True Blue");
menuItem3.addActionListener(mL);
menu.add(menuItem3);

// Füge das Menü der Menüleiste hinzu
menuBar.add(menu);
// Fügt die Menüleiste dem Frame hinzu
setJMenuBar(menuBar);

// Erzeuge das Listener-Objekt für die Werkzeugleiste
ToolBarListener tL = new ToolBarListener();

// Erzeuge die Werkzeugleiste
toolBar = new JToolBar("Rahmenfarbe");
// Erzeuge Buttons
button1 = new JButton("Schwarz");
button1.addActionListener(tL);
toolBar.add(button1);
button2 = new JButton("Grau");
button2.addActionListener(tL);
toolBar.add(button2);
button3 = new JButton("Weiss");
button3.addActionListener(tL);
toolBar.add(button3);


// Erzeuge das Label mit Initial-Bild
bildLabel = new JLabel(new ImageIcon("images/music.jpg"));

// Setze die Hintergrundfarbe des Containers
c.setBackground(Color.LIGHT_GRAY);
// Füge das Label dem Container hinzu
c.add(bildLabel, BorderLayout.CENTER);
c.add(toolBar, BorderLayout.NORTH);
}

class MenuListener implements ActionListener{
public void actionPerformed(ActionEvent e){
// Bildauswahl abhängig von der Aktion
if(e.getSource() == menuItem1)
bildLabel.setIcon(new ImageIcon("images/music.jpg"));
else if(e.getSource() == menuItem2)
bildLabel.setIcon(new ImageIcon("images/like_a_virgin.jpg"));
else if(e.getSource() == menuItem3)
bildLabel.setIcon(new ImageIcon("images/true_blue.jpg"));
}
}

class ToolBarListener implements ActionListener{
public void actionPerformed(ActionEvent e){
// Bildauswahl abhängig von der Aktion
if(e.getSource() == button1)
c.setBackground(Color.BLACK);
else if(e.getSource() == button2)
c.setBackground(Color.LIGHT_GRAY);
else if(e.getSource() == button3)
c.setBackground(Color.WHITE);
}
}

public static void main(String[] args){
Bilderrahmen fenster = new Bilderrahmen();
fenster.setTitle("Bilderrahmen");
fenster.setSize(360,440);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Programmiervarianten für die Ereignisverarbeitung

Wie oben bereits erwähnt, gibt es verschiedene Möglichkeiten, das in Java verwendete Modell der Ereignisverarbeitung programmtechnisch umzusetzen. Man kann dabei prinzipiell vier Varianten unterscheiden:

  1. Die Listener-Klasse wird als innere Klasse realisiert (wie in den bereits betrachteten Beispielen)
  2. Die Listener-Klasse wird als anonyme Klasse realisiert
  3. Die Container-Klasse wird selbst zur Listener-Klasse
  4. Die Listener-Klasse wird als separater Klasse realisiert

Die Varianten 3 und 4 wollen wir am Beipsiel unserer Klasse Farbwechsel näher erläutern.

Container-Klasse als Listener-Klasse

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/** Farbwechsel-Klasse selbst als Listener */
public class Farbwechsel extends JFrame implements ActionListener{

Container c;
JButton button;

public Farbwechsel(){
// Container bestimmen
c = getContentPane();
// Button erzeugen und dem Container hinzufügen
button = new JButton("Hintergrundfarbe wechseln");
c.add(button, BorderLayout.NORTH);

// Eigenes Objekt beim Button als Listener registrieren
button.addActionListener(this);
}

public void actionPerformed(ActionEvent e){
float zufall = (float) Math.random();
Color grauton = new Color(zufall, zufall, zufall);
c.setBackground(grauton); // Zugriff auf c möglich
}

public static void main(String[] args){
Farbwechsel fenster = new Farbwechsel();
fenster.setTitle("Farbwechsel");
fenster.setSize(200,100);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Listener-Klasse als separate Klasse

import java.awt.*;
import java.awt.event.*;

/** Eigenständige Listener-Klasse */
class ButtonListener implements ActionListener{

Container c; // Referenz auf den zu beeinflussenden Container

public ButtonListener(Container c){
this.c = c; // Referenz auf den zu beeinflussenden Container sichern
}

public void actionPerformed(ActionEvent e){
// Hintergrundfarbe des Containers zufällig ändern
float zufall = (float) Math.random();
Color grauton = new Color(zufall, zufall, zufall);
c.setBackground(grauton);
}
}

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/** Farbwechsel-Klasse mit separater Listener-Klasse */
public class Farbwechsel extends JFrame implements ActionListener{

Container c;
JButton button;

public Farbwechsel(){
// Container bestimmen
c = getContentPane();
// Button erzeugen und dem Container hinzufügen
button = new JButton("Hintergrundfarbe wechseln");
c.add(button, BorderLayout.NORTH);

// Eigenes Objekt beim Button als Listener registrieren
button.addActionListener(c);
}

public static void main(String[] args){
Farbwechsel fenster = new Farbwechsel();
fenster.setTitle("Farbwechsel");
fenster.setSize(200,100);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Drittes Beispiel: Euro-Rechner

import javax.swing.*;
import java.awt.event.*;

public class EuroRechner extends JPanel{

JTextField tf_euro, tf_sonst;
JComboBox cb_auswahl;

public EuroRechner(){
// Layout für unser Custom-JPanel festlegen
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

// Komponenten der Oberfläche erzeugen
tf_euro = new JTextField(20);
tf_sonst = new JTextField(20);
cb_auswahl = new JComboBox();

// Komponenten dem Custom-JPanel hinzufügen
add(tf_euro);
add(cb_auswahl);
add(tf_sonst);

// Auswahlliste mit Währungsnamen füllen
for(int i=0; i<12; i++){
cb_auswahl.addItem(EuroConverter.getBezeichnung(i));
}

// Listener-Objekt erzeugen
MyListener ml = new MyListener();

// und sowohl bei den Textfeldern
tf_euro.addActionListener(ml);
tf_sonst.addActionListener(ml);

// als auch bei der JComboBox registrieren
cb_auswahl.addActionListener(ml);
}

public class MyListener implements ActionListener {

double euro, sonst;

public void actionPerformed(ActionEvent e) {

// falls das Euro-Textfeld oder die Auswahlliste Auslöser des ActionEvents waren...
if(e.getSource() == tf_euro || e.getSource() == cb_auswahl){

// wir müssen von String nach double casten,
euro = Double.parseDouble(tf_euro.getText());

// weil convertToEuro als erstes Argument ein double-Wert erwartet.
sonst = EuroConverter.convertFromEuro(euro, cb_auswahl.getSelectedIndex());

// setText braucht einen String als Parameter
tf_sonst.setText("" + sonst);
}

// falls das Textfeld für die Fremdwährung Auslöser des ActionEvents war...
else if(e.getSource() == tf_sonst){
sonst = Double.parseDouble(tf_sonst.getText());
euro = EuroConverter.convertToEuro(sonst, cb_auswahl.getSelectedIndex());
tf_euro.setText("" + euro);
}
}
}

public static void main(String[] args){
JFrame fenster = new JFrame();
fenster.setTitle("Euro-Rechner");
fenster.setContentPane(new EuroRechner());
fenster.pack();
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fenster.setVisible(true);
}

}

public class EuroConverter {
	// Währungs-Kennungen
	static final int
		DEM = 0, ATS = 1, FRF = 2, BEF = 3, LUF = 4,  NLG = 5,
		ESP = 6, PTE = 7, ITL = 8, FIM = 9, IEP = 10, GDR = 11;
	
	// Umrechnungsfaktoren (von 2003)
	private static final double[] faktor = new double[] {
		1.95583, 13.7601, 6.55957, 40.3399, 2.20371,
		166.386, 200.482, 1936.27, 5.94573, 0.787564, 340.750
	};
	
	private static final String[] bezeichnung = new String[] {
		"Deutsche Mark", "Österreichische Schilling", "Französische Franc",
		"Belgische Franc", "Luxemburgische Franc", "Niederländische Gulden",
		"Spanische Peseten", "Portugiesische Escudos", "Italienische Lire",
		"Finnische Mark", "Irische Pfund", "Griechische Drachmen"
	};
	
	// liefert die Bezeichnung zur Währungs-Kennung 'kennung'
	static String getBezeichnung(int kennung){
		return bezeichnung[kennung];
	}
	
	// konvertiert den Euro-Wert 'euro' in die durch die
	// Währungs-Kennung 'kennung' spezifizierte Währung
	static double convertFromEuro(double euro, int kennung){
		return faktor[kennung] * euro;
	}
	
	// konvertiert den Wert 'sonst', der durch die
	// Währungs-Kennung spezifizierten Währung in den 
	// entsprechenden Euro-Wert
	static double convertToEuro(double sonst, int kennung){
		return sonst / faktor[kennung];
	}
	
}

» drucken: pdf | html

© 2004-2024 M. Blanke · Ursulaschule · Kleine Domsfreiheit 11-18 · 49074 Osnabrück