Ereignisse und deren Behandlung

 GUI Programme und Ereignisverarbeitung

GUI Programme laufen nicht linear ab. Bei Benutzerinteraktionen muss das Programm in der Lage sein sofort einen bestimmten Code zur Behandlung auszuführen.

Programmaktionen werden durch Benutzeraktionen getriggert. Man spricht hier von einem ereignisgesteuerten Programmablauf.

Definition: Ereignis

Ein Ereignis ist ein Vorgang in der Umwelt des Softwaresystems von vernachlässigbarer Dauer, der für das System von Bedeutung ist.

Im Rahmen dieses Abschnitts sprechen wir von einer wichtigen Gruppe von Ereignissen den Benutzerinteraktionen:

Beispiele sind

  • Mausclick
  • Tasteneingabe
  • Menülistenauswahl
  • Zeigen auf einen Bereich des GUI
  • Texteingabe oder Veränderung

Der Programmablauf wird aber auch von anderen weniger offensichtlichen Benutzerintreaktionen gesteuert

  • Verdecken des Programmfensters durch ein anderes Fenster
  • Fenstermodifikationen
    • Vergößern, verkleinern
    • Bewegen,
    • Schließen eines Fenster
  • Bewegen der Maus über das Programmfenster ohne Klicken (nicht auf allen Plattformen)
  • Erlangen des Fokus auf einem Fenster

Ereignisklassen

Java benutzt Klassen zur Behandlung von Ereignissen (engl. Events) der Java-Benutzeroberflächen. Typische Klassen sind

Die Klassen enthalten die Beschreibung von GUI Ereignissen (z.Bsp. Maus wurde auf Position x=17, y=24 geklickt).

Sie korrelieren mit den Klassen die die grafischen Komponenten implementieren: Z.Bsp.

Erzeugen von Ereignissen und deren Auswertung

Ereignisse werden automatisch von den Swingkomponenten erzeugt. Dies ist die Klasse JComponent mit ihren abgeleiteten Klassen. Sie werden zum Beispiel erzeugt, wenn ein Benutzer die Schaltfläche eines JButton betätigt. Die Aufgabe die dem Entwickler verbleibt ist die Registrierung seiner Anwendung für bestimmte GUI-Ereignisse. Die Anwendung kann nur auf Ereignisse reagieren gegen die sich sich vorher registriert hat.

Der Entwickler kann dann nach der Registrierung eines bestimmten Ereignisses ist die Auswertung des Ereignisses vornehmen und auf das Ereignis mit der gewünschten Aktion reagieren.

Java verwendet hierzu die Ereignis-Delegation um die Komponente die das Ereignis auslöst von der Ereignisbehandlung zu entkoppeln.

  • Der Benutzer betätigt z.Bsp. eine Schaltfläche
  • Das Laufzeitsystem erkennt das Ereignis
  • Objekte die das Ereignis beobachten (engl. "Listener" in Java) werden aufgerufen und das Ereignis wird behandelt.
    • Ein Listener erhält vom Laufzeitsystem ein ActionEvent-Objekt mit allen Informationen die zum Ereignis gehören.

Die Listenerobjekte müssen sich mit Hilfe einer Registrierung bei den GUI Objekten anmelden:

  • Komponenten die Ereignisse erzeugen (Klasse JComponent) können, erlauben die Registrierung von "Listener" Objekten (engl. zuhören; nicht nur hören!)
    • die Registrierung erfolgt mit Methoden der Syntax addXXXListener(...) der GUI Komponenten
  • "Listener" Objekte implementieren das Java Interface ActionListener und damit die Methoden die beim Eintritt eines Ereignisses ausgeführt werden sollen.

Wichtig: Beziehung zwischen Listenerobjekt und GUI Objekt

Ein Listenerobjekt kann sich gegen ein oder mehrere GUI Objekte registrieren

  • Registriert man ein Listenerobjekt gegen genau ein GUI Objekt (Bsp. 1 Button <-> 1 Listenerobjekt)
    • muß man den Urheber des Ereignisses und den Ereignistyp nicht analysieren. Der Typ des Ereignis und das GUI Objekt sind bekannt
  • Registriert man ein Listenerobjekt gegen mehrere GUI Objekte (Bsp. 3 Buttons <-> 1 Listenerobjekt)
    • muß man im Listenerobjekt das übergebene Ereignisobjekt auf den Verursacher, das GUI Objekt, untersuchen
    • muß man ein Listenerinterface implementieren, dass auch die Ereignisse aller Objekte verarbeiten kann.
      • Beispiel: 1 Button und ein Textfeld werden von einem Listenerobjekt verwaltet. Man benötigt hier eine gemeinsame Listener-Oberklasse die beide graphische Objekte verwalten kann

Beispiel

Das folgenden Beispiel ist eine sehr einfache Implementierung eines JFrame mit einem Button und einem ActionListener:

package s2.swing;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;

/**
*
* @author s@scalingbits.com
*/
public class ActionListenerBeispiel implements ActionListener {

   @Override
   public void actionPerformed(ActionEvent ae) {
      //Ausgabe des zum ActionEvent gehörenden Kontexts
      System.out.println("Aktion: " + ae.getActionCommand());
   }

   public static void main(String[] args) {
     JFrame myJFrame = new JFrame("Einfacher ActionListener");
     myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     JButton jb = new JButton("Hier drücken");

     ActionListenerBeispiel behandeln = new ActionListenerBeispiel();
     jb.addActionListener(behandeln); // Füge Listener zu Button
     myJFrame.add(jb); // Füge Button zu Frame
     myJFrame.pack();
     myJFrame.setVisible(true);
   } // Ende main()
} // Ende der Klasse

Die Klasse ActionListenerBeispiel implementiert die Methode actionPerformed() nach der Spezifikation eines ActionListener.

Das Programm startet in der main() Methode und erzeugt das folgende GUI:

 

  • Nach klicken des Button "feuert" das Buttonobjekt jb ein Ereignis (Event)
  • die Methode actionPerformed() des registrierten Listener behandeln wird mit einem Eventobjekt als Übergabeobjekt aufgerufen
  • Das Eventobjekt ae wird analysiert und das
  • Kommando wird als Text auf der Konsole ausgegeben:
Aktion: Hier drücken

Der Text "Hier drücken" wird ausgeben, da das Eventobjekt bei Buttons immer den Text des Buttons ausgibt.

Ereignisse (Events)

Es gibt eine reichhaltige Hierarchie von spezialisierten Event- und Listenerklassen. Hierzu sei auf die Java-Tutorials von Oracle verwiesen.

Weitere Events sind zum Beispiel:

  • ItemEvent: Z. Bsp. Analysieren von JCheckBox Komponenten
  • MouseEvent: Analysieren von Mausposition, Bewegung etc. 
  • ChangeEvent: Z. Bsp. Analysieren von Änderungen an einem JSlider

Der Swing "Event Dispatch Thread"

Damit Benutzeraktionen unabhängig vom normalen Programmablauf behandelt werden können, benutzt Swing eine eigene Ausführungseinheit, einen Thread, zum Bearbeiten der Benutzeraktionen. Dieser "Thread" ist ein Ausführungspfad der parallel zum normalen Programmablauf im main-Thread abläuft. Threads laufen parallel im gleichen Adressraum des Prozesses und haben daher Zugriff auf die gleichen Daten.

Blockieren des GUIs

Alle Aktionen der ActionListener werden vom "Swing-Event-Dispatch-Thread" ausgeführt. Diese Thread  arbeitet GUI Interaktionen ab während das Javaprogramm mit anderen Threads im Hintergrund weiterlaufen kann.

Wichtig

Alle Codestrecken von zur Behandlung von Ereignissen (Methode actionPerformed()) werden in nur einem Thread aufgerufen und blockieren alle anderen Behandlungen während sie ausgeführt werden.

Im Klartext: Das GUI wird während der Ausführungszeit einer Ereignisbehandlung nicht bedient und ist blockiert.

Vermeiden Sie aufwändige (=langlaufende)  Implementierungen in den actionPerformed() Methoden!

Beispiel

Das folgende Programm blockiert das GUI für 2 Sekunden. Die Blockade ist nach dem Klicken an der geänderten Farbe des Buttons zu erkennen. Er bleibt gedrückt:

package s2.swing;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;

/**
*
* @author s@scalingbits.com
*/
public class ActionListenerBlockiert implements ActionListener {
   @Override
   public void actionPerformed(ActionEvent ae) {
     //Ausgabe des zum ActionEvent gehörenden Kontexts
     System.out.println("Aktion: " + ae.getActionCommand());
     try {// Thread für 2s blockieren (schlafen)
         Thread.sleep(2000);
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
   }

   public static void main(String[] args) {
      JFrame myJFrame = new JFrame("Einfacher ActionListener");
      myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      JButton jb = new JButton("Hier drücken");
      ActionListenerBlockiert behandeln = new ActionListenerBlockiert();
      jb.addActionListener(behandeln); // Füge Listener zu Button
      myJFrame.add(jb); // Füge Button zu Frame
      myJFrame.pack(); // Berechne Layout
      myJFrame.setVisible(true);
   }
}

Synchronisation mit Swing GUIs

Nachdenklicher Duke
Wichtig
Die meisten Swing Komponenten sind nicht synchronisiert. Veränderungen an den Datenstrukturen durch nicht synchronisiertern Zugriff von anderen Threads können zu Inkonsistenzen führen.

 

 

Anonymous (not verified)

Mon, 06/24/2013 - 16:31

Sollte der zweite Absatz unter der Überschrift eigentlich so formuliert sein?
"Der Entwickler kann dann nach der Registrierung eines bestimmten Ereignisses die Auswertung des Ereignisses vornehmen und auf das Ereignis mit der gewünschten Aktion reagieren."

Und beim ersten Beispiel steht: "Nach klicken des Button "feuert" ..."
Dort ist der Button "Hier klicken" gemeint, oder?

Anonymous (not verified)

Thu, 06/27/2013 - 22:55

In dem Beispiel:

ActionListenerBeispiel behandeln = new ActionListenerBeispiel();
--> jb.addActionListener(behandeln); // Füge Listener zu Button
myJFrame.add(jb); // Füge Button zu Frame
myJFrame.setVisible(true);
myJFrame.pack();

müsste der Listener der auf jb registriert wird, "ActionListenerBeispiel" heißen oder?

Ich bin mir nicht ganz sicher, dass wir über das gleiche sprechen.
Kommunikation über diese Kommentare haben ihre Grenzen.

behandeln ist ein Zeiger auf ein Objekt ActionListenerBeispiel.
Dieses Objekt wird beim dem Objekt jb mit addActionListener registriert.

Das Programm übersetzt und funktioniert auch.