Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


9.2.8

Die Ereignisse im Einzelnen



Nachdem auf den vorherigen Seiten mehr theoretisch die grundlegenden Prinzpien der Ereignisverarbeitung ab dem JDK 1.1 behandelt wurden, wird es im folgenden etwas praktischer. In den nächsten Abschnitten werden die einzelnen Ereignistypen beschrieben und erläutert, wie in der Praxis eine Reaktion auf ein konkretes Ereignis aussehen kann.

Action-Events

Ein Action-Event wird ausgelöst, wenn in einer Komponente eine Aktion geschieht. Das ist zum Beispiel der Fall, wenn ein Button gedrückt, die Eingabe in ein Textfeld durch Return abgeschlossen wird, oder die Auswahl eines Elements aus einer List, aus einem Menü oder aus einer Choice erfolgt.

Ein Action-Event wird in Java durch die Klasse ActionEvent repräsentiert, und die Reaktion auf ein solches Ereignis ist durch Implementierung des Interface ActionListener möglich.

Eine Auflistung der wichtigsten Methoden zum Zugriff auf Informationen eines ActionEvent-Objekts zeigt Tabelle 9.2.

Tabelle 9.2: Informationen eines Action-Events
MethodeInformation
String getActionCommand()Das Kommando zu dieser Aktion
int getModifiers()Modifier-Tasten (Meta, Esc oder Shift), die während der Aktion gedrückt wurden

Folgendes Beispiel implementiert das oben genannte Interface. Das Beispiel-Applet besitzt zwei Buttons, einen mit der Aufschrift »open« und einen mit der Aufschrift »close«. Wird einer der Buttons gedrückt, so wird ein Text mit der Aufschrift des gedrückten Buttons in der Statuszeile des Applets ausgegeben:

  import java.applet.Applet;
  import java.awt.*;
  import java.awt.event.*;
  
  public class ActionEventDemo extends Applet
                               implements ActionListener {
  
    public void init () {
      // Erzeugen und Hinzufügen der Buttons
      Button open = new Button("open");
      Button close = new Button("close");
      add(open);
      add(close);
      open.addActionListener(this);
      close.addActionListener(this);
      open.setActionCommand("open");
      close.setActionCommand("close");
    }
  
    // Aufruf bei Druck eines Buttons
    public void actionPerformed(ActionEvent e) {
      // Ist 'open' gedrückt?
      if ("open".equals(e.getActionCommand()))
        // Wenn ja, zeige Text an
        getAppletContext().showStatus("action in open");
      // Ist 'close' gedrückt?
      if ("close".equals(e.getActionCommand()))
        // Wenn ja, zeige Text an
        getAppletContext().showStatus("action in close");
    }
  
  }
In diesem Beispiel implementiert die Applet-Klasse direkt das ActionListener-Interface. Das ist in diesem Fall recht einfach zu bewerkstelligen, da ActionListener nur die Methode actionPerformed(ActionEvent) besitzt. Diese Methode muss in jedem Fall für eine Ereignisbehandlung implementiert werden.

Durch Aufruf der Methode addActionListener(ActionEvent) der beiden Buttons mit dem this-Operator als Argument wird das implementierte ActionListener-Interface bei der Ereignisquelle registriert.
  open.addActionListener(this);
  close.addActionListener(this);
In diesem Fall registriert sich ein EventListener bei zwei Ereignisquellen, was durchaus zulässig ist. Dadurch benutzen beide Ereignis-Quellen dieselbe Behandlungsroutine. Es ist somit in der Behandlungsroutine eine Unterscheidung zwischen den beiden Ereignisquellen zu treffen.

Im obigen Beispiel wird die Unterscheidung durch Aufruf der Methode getActionCommand() des übergebenen ActionEvent-Objektes getroffen. getActionCommand() liefert einen String als Ergebnis, der Informationen zu dem Objekt enthält, in dem das Ereignis ausgelöst wurde. Beim Drücken auf einen Button liefert ein Aufruf von getActionCommand() des erzeugten ActionEvent-Objektes den String, der zuvor mit setActionCommand() gesetzt wurde.
  open.setActionCommand("open");
  close.setActionCommand("close");
Dadurch ist eine eindeutige Unterscheidung der beiden Buttons innerhalb von actionPerformed(ActionEvent) durch einen Aufruf von getActionCommand() m"oglich:
  if ("open".equals(e.getActionCommand()))
Jeder String in Java ist ein Objekt, auch die String-Literale. Deshalb ist es möglich, direkt die equals()-Methode des Literals aufzurufen. Die equals()-Methode eines Objektes prüft zuerst, ob das Argument ein Exemplar der gleichen Klasse wie das eigene Objekt ist. Ist dies der Fall, werden deren Werte auf Gleichheit geprüft Abschnitt.

Wird einem Button kein Name durch Aufruf von setActionCommand() zugewiesen, liefert ein Aufruf von getActionCommand() die Aufschrift des Buttons zurück. Hierdurch kann natürlich auch die Unterscheidung zwischen zwei Buttons vorgenommen werden, es sollte jedoch beachtet werden, dass damit die Entwicklung von mehrsprachigen Anwendungen erschwert wird. Von einer Übersetzung in eine andere Sprache sind in der Regel auch die Aufschriften von Buttons betroffen. Bei einer Änderung der Aufschrift müsste somit auch der Code zur Reaktion auf ein Ereignis angepasst werden. Durch explizites Zuweisen eines Namens mit setActionCommand() kann man den Code zur Ereignisbehandlung unabhängig von der Aufschrift des Buttons und somit sprachunabhängig gestalten. Die Methode setActionCommand() kann auch bei den Komponenten TextField und MenuItem verwendet werden.

Doch wie muss vorgegangen werden, wenn ein Action-Event in einem Exemplar von Choice oder List auftritt?

Hierzu muss man ebenfalls wissen, in welcher Komponente das Ereignis stattgefunden hat. Eine Abfrage über die Methode getActionCommand() ist in diesem Fall nicht möglich, da diese Methode den ausgewählten Eintrag als Ergebnis liefert, wenn die Aktion in einer Choice oder List ausgelöst wurde.

In diesem Fall kann man direkt die Quelle des Ereignisses abfragen. Die Methode getSource(), die in der Klasse EventObject definiert ist, liefert immer einen Verweis auf das Objekt, in dem sich ein Ereignis zugetragen hat. Verwendet das Programm beispielsweise nur eine Choice, so kann man mit einer einfachen Überprüfung sicherstellen, dass man richtig auf das eingetretene Ereignis reagiert.

Im folgenden Beispiel wurde die actionPerformed()-Methode des letzten Beispiels um eine Abfrage erweitert. Dem Beispielprogramm wurde noch eine Choice hinzugefügt, deren Auswahl ebenfalls berücksichtigt werden soll:
  public void actionPerformed(ActionEvent e){
    // Hat sich das Event in einer Choice ereignet?
    if (e.getSource() instanceof Choice) {  
      // Wenn ja, zeige ausgewählten  Eintrag an
      getAppletContext().showStatus(
                     "Choice: "+e.getActionCommand());          
      return;
    }
    // Ist 'open' gedrückt?
    if ("open".equals(e.getActionCommand())) 
      // Wenn ja, zeige Text an
      getAppletContext().showStatus("action in open");
    // Ist 'close' gedrückt?
    if ("close".equals(e.getActionCommand()))
      // Wenn ja, zeige Text an
      getAppletContext().showStatus("action in close"); 
  }
Zunächst wird überprüft, ob das ausgelöste Ereignis von der Choice stammt:
  if (e.getSource() instanceof Choice)
In diesem Fall wird der Eintrag angezeigt und die Methode verlassen. Stammt das Ereignis von einem der Buttons, dann werden dieselben Abfragen wie im letzten Beispiel durchgeführt. Es ist zu beachten, dass in diesem Beispiel die Reihenfolge der Abarbeitung in actionPerformed(ActionEvent) eine Rolle spielt. Würde man zuerst auf obige Weise überprüfen, ob das Ereignis in einem der Buttons ausgelöst wurde, kann es Probleme geben, wenn in der Choice Einträge mit demselben Text vorhanden sind wie eines der Labels der Buttons. Beim Auswahl eines Eintrags aus einer List verhält sich dies genauso.

Deklariert eine Klasse mehrere Komponenten gleicher Art, z. B. mehrere Exemplare von Choice, kann nach dieser Methode nicht eindeutig auf die Komponente geschlossen werden, in der das Ereignis ausgelöst wurde.

Beim obigen Beispiel würde dies bedeuten, dass in der Klasse, in der sich die actionPerformed()-Methode befindet, ein weiteres Exemplar von Choice deklariert ist. Durch die Abfrage mit instanceof wird nur geprüft, ob es sich um ein Exemplar von Choice handelt. Es kann hierdurch jedoch nicht zwischen zwei Exemplaren von Choice unterschieden werden.

Ist eine Unterscheidung notwendig, muss man die Abfrage direkt durch einen Vergleich des Objektes, in dem das Ereignis ausgelöst wurde, mit einem Verweis auf die Komponenten, die dafür in Frage kommen, durchgeführt werden.

Folgende Abfrage befindet sich in einer Klasse, die ein Exemplar von Choice mit dem Namen myChoice besitzt:
  if (e.getSource() == myChoice)

Ist diese Bedingung true, kann man sicher sein, dass das Ereignis in myChoice ausgelöst wurde.

Um zwei Exemplare der Klasse TextField bei der Lokalisierung der Ereignisquelle zu unterscheiden, kann man sich ebenfalls der Methode getSource() bedienen.

Folgendes Beispiel zeigt, wie dies gemacht wird:
  import java.applet.Applet;
  import java.awt.*;
  import java.awt.event.*;
  
  public class DoubleTextFieldDemo extends Applet
                               implements ActionListener {
    TextField topField, bottomField;
    Label messageLabel; // Label zur Ausgabe einer Meldung
  
    public void init() {
      // Erzeugen der Komponenten
      topField = new TextField();
      bottomField = new TextField();
      messageLabel = new Label();
      messageLabel.setAlignment(Label.CENTER);
      // Hinzufügen der Komponenten
      setLayout(new BorderLayout());
      add("North", topField);
      add("South", bottomField);
      add("Center", messageLabel);
      // Applet als Listener registrieren
      topField.addActionListener(this);
      bottomField.addActionListener(this);
    }
  
    public void actionPerformed(ActionEvent e) {
      // Wurde in 'topField' etwas eingegeben?
      if (e.getSource() == topField)
        // Wenn ja, setze Text
        messageLabel.setText("top text field");
      // Wurde in 'bottomField' etwas eingegeben?
      if (e.getSource() == bottomField)
        // Wenn ja, setze Text
        messageLabel.setText("bottom text field");
    }
  }
Eine weitere gute Möglichkeit zur Unterscheidung der Ereignisquellen besteht darin, anonyme Klassen als Event-Listener einzusetzen. Die Registrierung der ActionListener hat in diesem Fall folgenden Quellcode:
  // Applet als Listener registrieren
  topField.addActionListener(new ActionListener() {
  	public void actionPerformed(ActionEvent e) {
  		setMessage("top text field");
  	}
  });
  bottomField.addActionListener(new ActionListener() {
  	public void actionPerformed(ActionEvent e) {
  		setMessage("bottom text field");
  	}
  });
Die Reaktion auf das Ereignis wurde hier in die Methode setMessage() ausgelagert. setMessage() zeigt die Zeichenkette, die als Argument übergeben wird, innerhalb des Label an:
  public void setMessage(String s) {
    messageLabel.setText(s);
  }

Für jede Ereignisquelle wird hier ein eigenes Exemplar einer anonymen Implementierung des ActionListener-Interface erzeugt. Hierdurch wird bei jedem der zwei Ereignisse eine andere actionPerformed()-Methode aufgerufen. Da setMessage() in den zwei Implementierungen unterschiedliche Parameter übergeben bekommt, wird jeweils ein anderer Text angezeigt.

Maus-Events

Maus-Events werden ausgelöst, sobald der Benutzer eine Maustaste betätigt oder die Maus bewegt.

Für die Behandlung von Maus-Events sind im Paket java.awt.event zwei Listener-Interfaces definiert: Sowohl das Interface MouseListener als auch das Interface MouseMotionListener definieren mehrere Methoden, die für die Reaktion auf die unterschiedlichen Ereignisse implementiert werden müssen.

Zunächst werden die Methoden des Interface MouseListener beschrieben: Für die Auslösung eines mouseClicked-Ereignisses muss eine Maustaste innerhalb einer kurzen Zeit gedrückt und wieder losgelassen werden. Hierbei ist zu beachten, dass vor dem Aufruf von mouseClicked zunächst mousePressed und mouseReleased aufgerufen werden, da die Maustaste hierzu zunächst gedrückt und wieder losgelassen werden muss.

Das Interface MouseMotionListener definiert nur zwei Methoden: Maus-Events werden in Java durch Objekte der Klasse MouseEvent repräsentiert. Alle Methoden der oben genannten Interfaces besitzen einen Parameter vom Typ MouseEvent. Über die Methoden in Tabelle 9.3 können Informationen von einem MouseEvent-Objekt erfragt werden.

Tabelle 9.3: Informationen eines Maus-Events
MethodeInformation
int getX()Die x-Position des Ereignisses
int getY()Die y-Position des Ereignisses
int getClickCount()Die Anzahl der aufeinanderfolgenden Mausklicks
boolean isPopupTrigger()true, wenn die Standardtaste für das Anzeigen von Popup-Menüs gedrückt wurde

Die Interfaces MouseListener und MouseMotionListener besitzen jeweils mehr als eine Methode. Wenn man nur eine dieser Methoden für die Ereignisverarbeitung benötigt, ist eine einfache Implementierung, wie es im Abschnitt über Action-Events gezeigt wurde, nicht möglich. Für die nicht benötigten Methoden müssten Dummy-Implementierungen bereitgestellt werden.

Um Dummy-Implementierungen zu vermeiden, wird eine neue, von der Klasse MouseMotionAdapter abgeleitete Klasse als Event-Adapter definiert:
  import java.applet.Applet;
  import java.awt.*;
  import java.awt.event.*;
  
  public class MouseEventDemo extends Applet {
    Rectangle rect;      // Anzeigebereich
  
    public void init() {
      rect = new Rectangle(90, 20, 70, 50);
      addMouseMotionListener(new DemoMouseMotionAdapter(this));
    }
  
    public void paint(Graphics g) {
      // Zeichnet ein blau ausgefülltes Rechteck
      g.setColor(Color.blue);
      g.fillRect(rect.x, rect.y, rect.width, rect.height);
    }
  
   // Aufruf bei Bewegung der Maus
   public void isInside(int x, int y) {
    // Ist die Maus innerhalb des Rechtecks?
    if (rect.contains(x,y))
      // Wenn ja, setze Text auf 'Mouse is in'
      getAppletContext().showStatus("Mouse is in");
    else
      // sonst setze Text auf 'Mouse is out'
      getAppletContext().showStatus("Mouse is out");
    }
  }
  
  class DemoMouseMotionAdapter extends MouseMotionAdapter {
    protected MouseEventDemo demo;
  
    public DemoMouseMotionAdapter(MouseEventDemo demo) {
      this.demo = demo;
    }
  
    public void mouseMoved(MouseEvent e) {
      demo.isInside(e.getX(), e.getY());
    }
  }
Das Applet besitzt im Zentrum ein blaues Rechteck. Wird die Maus über das Rechteck bewegt, so erscheint in der Statuszeile des Applets der Text »Mouse is in«, andernfalls wird dort »Mouse is out« ausgegeben.

In der init()-Methode des Applets erfolgt die Registrierung als MouseMotionListener. In diesem Fall wird allerdings nicht direkt das Applet als Listener registriert, sondern eine neu definierte Adapterklasse. Diese Adapterklasse ist von der im Paket java.awt.event definierten Klasse MouseMotionAdapter abgeleitet. Der abgeleiteteten Klasse wird als Parameter ein Verweis auf das Applet übergeben. Dieser Verweis wird innerhalb der Adapterklasse in einem Feld gespeichert.

Wenn man die Maus bewegt, wird die mouseMoved(MouseEvent)-Methode der neu definierten Adapterklasse aufgerufen. Die eigentliche Reaktion auf das Ereignis wird aber in der Methode isInside() des Applets vorgenommen. Diese Methode wird nämlich aus mouseMoved(MouseEvent) mit den Koordinaten des Mauszeigers als Parameter aufgerufen. Die Koordinaten können über die Methoden getX() bzw. getY() von dem MouseEvent-Objekt erfragt werden:
  public void mouseMoved(MouseEvent e) {
    demo.isInside(e.getX(), e.getY());
  }

Die Anzeige des Textes wird schließlich in isInside(int, int) veranlasst.

Eine andere wichtige Eigenschaft der Maus-Events betrifft die Aktivierung von Popup-Menüs. Ein Popup-Menü wird auf jeder Plattform durch einen Maustastendruck angezeigt. Leider ist die Taste, die dazu gedrückt wird, nicht auf allen Systemen einheitlich. So wird z. B. auf Windows und Unix jeweils die rechte Maustaste verwendet, aber auf Macintosh üblicherweise die Maustaste in Verbindung mit einem Drücken der Alt-Taste. Ein Drücken der linken Maustaste ist auf Macintosh meist nicht möglich, da dort sehr oft nur eine Maus mit einer Taste vorhanden ist.

Damit der Programmierer die plattformabhängige Tastenkombination zum Anzeigen von Popup-Menüs verwenden kann, ohne Abfragen vornehmen zu müssen, ist in der Klasse MouseEvent die Methode isPopupTrigger() definiert. Diese Methode liefert true, wenn die Tastenkombination aktiviert wurde, die auf dem betreffenden System standardmäßig das Aktivieren von Popup-Menüs übernimmt.

Dieser Sachverhalt wird im folgenden Beispiel demonstriert:
  import java.awt.*;
  import java.awt.event.*;
  import java.applet.*;
  
  public class PopupDemo extends Applet
                               implements MouseListener {
    PopupMenu pop;
  
    public void init() {
      setBackground(Color.blue);
      pop = new PopupMenu("Context");
      pop.add("forward");
      pop.add("back");
      pop.addSeparator();
      pop.add("edit");
      add(pop);
      addMouseListener(this);
    }
  
    public void mousePressed(MouseEvent e) {
      if (e.isPopupTrigger())
        pop.show(this, e.getX(), e.getY());
    }
  
    public void mouseReleased(MouseEvent e) {
      if (e.isPopupTrigger())
        pop.show(this, e.getX(), e.getY());
    }
  
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
  }
In diesem Beispiel wird ein wenig anders vorgegangen als im letzten: Die Reaktion auf ein Maus-Ereignis erfolgt nicht mit einem MouseListener, sondern in der Methode processMouseEvent(). Das wird deshalb gemacht, weil es nicht vorhersehbar ist, bei welchem Maus-Ereignis ein Popup-Menü auf einer bestimmten Plattform angezeigt wird. Bei Motif werden Popup-Menüs normalerweise beim Drücken der Maustaste angezeigt, bei Windows beim Loslassen der Maustaste. Würde man durch Implementierung des MouseListener-Interfaces ein Popup-Menü anzeigen, so müsste man sowohl in mousePressed() als auch in mouseReleased(), damit rechnen, dass ein Popup-Menü angezeigt werden muss.
  public void mousePressed(MouseEvent e) {
    if (e.isPopupTrigger())
      pop.show(this, e.getX(), e.getY());
  }

  public void mouseReleased(MouseEvent e) {
    if (e.isPopupTrigger())
      pop.show(this, e.getX(), e.getY());
  }

  public void mouseClicked(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}

Ist es erforderlich, bei Maus-Ereignissen z. B. auf Doppelklicks zu reagieren, kann die Methode getClickCount() der Klasse MouseEvent aufgerufen werden. Diese Methode liefert die Anzahl der aufeinanderfolgenden Klicks.

Beim Aufrufen von getClickCount() sollte jedoch darauf geachtet werden, dass jedem Doppelklick ein MouseEvent mit Einfachklick vorausgeht: Wird die Maustaste gedrückt, erzeugt dies ein MouseEvent mit getClickCount() gleich 1. Erst wenn man die Maustaste innerhalb eines bestimmten Zeitraums noch einmal klickt, erhält man zusätzlich ein MouseEvent mit getClickCount() gleich 2.

Alle Methoden zur Behandlung von Maus-Events werden unabhängig von der Maustaste, die das Ereignis ausgelöst hat, aufgerufen. Um zu ermitteln, welche Maustaste betätigt wurde, muss die Methode getModifiers()des übergebenen MouseEvent-Objektes abgefragt werden. Dieses Methode liefert je nach Maustaste einen bestimmten Wert:

Tabelle 9.4: Modifier für Maus-Events
MaustasteModifier
LinksInputEvent.BUTTON1_MASK
MitteInputEvent.BUTTON2_MASK
RechtsInputEvent.BUTTON3_MASK

Key-Events

Ein Key-Event wird erzeugt, sobald der Benutzer eine Taste auf der Tastatur drückt. Zusätzlich zum eigentlichen Tastencode kann der Status der Modifiers (Control, Shift, Meta (Taste abhängig vom System: meist Esc oder Alt)) berücksichtigt werden. Zur Reaktion auf Key-Events über das Delegationsmodell muss das Interface KeyListener implementiert werden.

KeyListener enthält drei Methoden: Der Unterschied zwischen keyPressed(KeyEvent) und keyTyped(KeyEvent) besteht darin, dass keyPressed(KeyEvent) bereits beim Drücken einer Taste aufgerufen wird. Um keyTyped(KeyEvent) zu aktivieren, muss man eine Taste innerhalb eines kurzen Zeitraums drücken und wieder loslassen (analog zu mouseClicked() bei der Behandlung von Mausereignissen). Vor dem Aufruf von keyTyped(KeyEvent) wird immer zunächst keyPressed(KeyEvent) aufgerufen. Anschließend erfolgt ein Aufruf von keyTyped(KeyEvent) und zu guter Letzt wird keyReleased(KeyEvent) ausgeführt.

Das Drücken von Tasten, denen kein Zeichen zur Ausgabe zugewiesen ist (z. B. F1 bis F12), erzeugt keinen Aufruf von keyTyped(KeyEvent). In diesem Fall werden nur keyPressed(KeyEvent) und keyReleased(KeyEvent) aufgerufen.

Alle der oben vorgestellten Methoden besitzen einen Parameter vom Typ KeyEvent. Die Klasse KeyEvent enthält die Informationen, die für einen Key-Event wichtig sind. Über welche Methoden diese Informationen abgefragt werden können, ist in Tabelle 9.5 dargestellt.

Tabelle 9.5: Informationen eines Key-Events
MethodeInformation
int getKeyCode()Code der gedrückten Taste
char getKeyChar()Zeichen, das der gedrückten Taste entspricht

Eine Anwendung könnte z. B. folgendermaßen aussehen:

  import java.applet.Applet;
  import java.awt.*;
  import java.awt.event.*;
  
  public class KeyEventDemo extends Applet implements KeyListener {
    TextArea messages;
  
    public void init() {
      // Erzeugen der Komponenten
      messages = new TextArea();
      // Hinzufügen der Komponenten
      setLayout(new BorderLayout());
      add("Center", messages);
      messages.addKeyListener(this);
    }
  
    public void keyPressed(KeyEvent e) {
      messages.append("pressed\n");
      setKeyState(e);
    }
  
    public void keyReleased(KeyEvent e) {
      messages.append("released\n");
      setKeyState(e);
    }
  
    public void keyTyped(KeyEvent e) {
      messages.append("typed\n");
      setKeyState(e);
    }
  
    private void setKeyState(KeyEvent e) {
      String modifierText = "";
      // Ist die Shift-Taste gedrückt?
      if (e.isShiftDown())
        modifierText = "Shift  ";
      // Ist die Control-Taste gedrückt?
      if (e.isControlDown())
        modifierText = modifierText + "Control  ";
      // Ist die Meta-Taste gedrückt?
      if (e.isMetaDown())
        modifierText = modifierText + "Meta  ";
      // Setzen des Textes
      String keyCode, keyChar;
      keyCode = "KeyCode = "+e.getKeyCode();
      keyChar = "KeyChar = "+e.getKeyChar();
      messages.append("Modifiers: "+modifierText+"\n");
      messages.append(keyCode+"\n");
      messages.append(keyChar+"\n\n");
      e.consume();
    }
  }
Das Applet reagiert auf alle drei Key-Events. Bei Drücken einer Taste wird jeweils die Art des ausgelösten Ereignisses, die gedrückten Modifier-Tasten und der Tastencode der Taste in einer TextArea ausgegeben. Beim Start des Applets ist zu beachten, dass die Ausgabe nur angezeigt wird, wenn die TextArea den Fokus besitzt. Das liegt daran, dass die Registrierung als KeyListener bei der TextArea erfolgt:
  messages.addKeyListener(this);
Hat die TextArea den Fokus einmal, bekommt sie alle Tastatureingaben übermittelt und kann somit die KeyEvent-Objekte an ihre Listener senden.

In jeder der drei Methoden zur Behandlung der Eingabe wird jeweils die Art des Ereignisses und anschließend von der Methode setKeyState(KeyEvent) die gedrückten Modifier-Tasten und der Tastencode ausgegeben. Nach der Ausgabe wird das Event-Objekt ebenfalls in der Methode setKeyState(KeyEvent) konsumiert:
  e.consume();
Dadurch wird die Standardreaktion auf eine Eingabe in die TextArea unterdrückt. Da die TextArea in diesem Beispiel editierbar gestaltet wurde, würde ohne Konsumieren des Ereignis-Objektes bei jedem Tastendruck das Zeichen der gedrückten Taste ausgegeben werden. Das kann auch durch Konsumieren des Ereignis-Objekts verhindert werden. Das Konsumieren des Ereignis-Objekts sollte aber in jedem Fall durch Aufruf der Methode keyTyped(KeyEvent) ausgeführt werden. Das Anzeigen des Textes geschieht nämlich nach Aufruf von keyTyped(KeyEvent) und vor dem Aufruf von keyReleased(KeyEvent). Das Zusammenspiel zwischen dem Drücken einer Taste und dem Anzeigen des Textes in einer Textkomponente wird nochmals ausführlich im Abschnitt 9.2.8 dargestellt. Bei einer editierbaren Textkomponente besteht die Standardreaktion im Anzeigen des eingegebenen Textes.

Da in dem Interface KeyListener mehr als eine Methode definiert ist, existiert im Paket java.awt.event eine Adapterklasse mit dem Namen KeyAdapter. Sie wird analog zu den anderen Adapter-Klassen verwendet.

Damit die Abfrage von Tastencodes nicht von bestimmten Zeichensatz-Codes abhängt, sind in der Klasse KeyEvent so genannte virtuelle Tastencodes definiert. Virtuelle Tastencodes sind Konstanten, die jeweils eine Taste auf der Tastatur repräsentieren. Dieser Code wird von der Methode getKeyCode() zurückgeliefert. Eine kleine Auswahl der virtuellen Tastencodes ist in Tabelle 9.6 enthalten.

Tabelle 9.6: Virtuelle Key-Codes
KonstanteTaste
KeyEvent.VK_0-KeyEvent.VK_9Ziffern 0 bis 9
KeyEvent.VK_A-KeyEvent.VK_ZBuchstaben A bis Z
KeyEvent.VK_DOWNCursor unten
KeyEvent.VK_LEFTCursor links
KeyEvent.VK_RIGHTCursor rechts
KeyEvent.VK_HOMEHome-Taste
KeyEvent.VK_ENDEnde-Taste
KeyEvent.VK_INSERTEinfügen-Taste
KeyEvent.VK_DELETEEntfernen-Taste
KeyEvent.VK_PAGE_DOWNBild-hoch-Taste
KeyEvent.VK_PAGE_UPBild-runter-Taste
KeyEvent.VK_SPACELeertaste
KeyEvent.VK_F1-KeyEvent.VK_F12Funktionstasten 1 bis 12

Es ist jedoch zu beachten, dass die Keycodes nur beim Aufruf von keyPressed() und keyReleased() eine Rolle spielen. Beim Aufruf von keyTyped() liefert die Methode getKeyCode() des übergenen KeyEvent-Exemplars immer 0 als Ergebnis. Das liegt daran, dass keyTyped() nur bei druckbaren Zeichen eine Rolle spielt. keyPressed() und keyReleased() werden jedoch bei jedem Tastendruck aufgerufen. Aus diesem Grund sollte man beim Aufruf von keyTyped() nicht den Keycode abfragen, sondern direkt das Zeichen über die Methode getKeyChar() ermitteln.

Möchte man dennoch beim Aufruf von keyTyped() den Keycode wissen, kann den keyPressed()-Aufruf abfangen, der keyTyped() vorausgeht. Im vorausgehenden keyPressed() und dem nachfolgenden keyReleased() ist der Keycode nämlich verfügbar.

Selection-Events

Bei allen Swing-Komponenten, bei denen eine Selektion bestimmter Daten vorgenommen werden kann (JList, JTable und JTree), ist es möglich, dass der Entwickler einen ListSelectionListener bzw. TreeSelectionListener registriert.

Das Listener-Interface bietet dabei lediglich die Methode
 public void valueChanged(ListSelectionEvent e);
für die JList und JTable bzw.
 public void valueChanged(TreeSelectionEvent e);
für den JTree.

Über die Methoden getFirstIndex() und getLastIndex() erhält man beim ListSelectionEvent den Abschnitt, der selektiert bzw. deselektiert wurde. Wenn das ListSelectionEvent Teil einer größeren Selektionsänderung ist, liefert die Methode getValueIsAdjusting() true zurück.

Das folgende Beispiel zeigt das jeweils selektierte Element einer JList an.

 JList list = new JList(
   new Object[] {"A","B","C"});
 // Nur jeweils ein Datum selektieren lassen
 list.setSelectionMode(
   ListSelectionModel.SINGLE_SELECTION);
 list.addListSelectionListener(
   new ListSelectionListener() {
     public void valueChanged(ListSelectionEvent e) {
       Object selectedItem = ((JList) e.getSource()).
         getSeletedValue();
       if (selectedValue != null)
         System.out.println(selectedItem);
     }
   });

Das TreeSelectionEvent gibt im Gegensatz dazu Auskunft darüber, wie sich die Selektion im Baum verändert hat. Das Event bietet Informationen, welche Pfade sich geändert haben und ob es sich um eine Selektion oder Deselektion handelt (über die Methoden isAddedPath() und isAddedPath(int index)).

Das folgende Beispiel liefert bei einer Selektion im Baum alle neu hinzugefügten Pfade:

 JTree tree = new JTree();
 tree.addTreeSelectionListener(
   new TreeSelectionListener() {
     public void valueChanged(TreeSelectionEvent e) {
       TreePath[] paths = e.getPaths();
       for (int i = 0; i < paths.length; i++)
         if (e.isAddedPath(i))
           System.out.println(
             "Neu: "+paths[i].toString());
     }
   });

Item-Events

Ein Item-Event wird erzeugt, sobald der Benutzer eine Selektion in einer Komponente vornimmt. Eine solche Komponente bietet dem Benutzer typischerweise eine bestimmte Anzahl von Elementen zur Auswahl an. Der Benutzer kann eines bzw. mehrere Elemente aktivieren und wieder deaktivieren.

Alle Komponenten, die eine Selektion unterstützen, implementieren das Interface ItemSelectable. Unter den vordefinierten Komponenten gehören dazu Checkbox, CheckboxMenuItem, Choice und List.

Objekte, die von einer Item-Operation benachrichtigt werden wollen, müssen das Interface ItemListener implementieren. ItemListener definiert folgende Methode: Als Parameter erhält itemStateChanged(ItemEvent) das Ereignis ItemEvent-Objekt des Ereignisses übergeben. In Tabelle 9.7 sind die Methoden aufgeführt, die dem Programmierer Zugriff zu den Informationen bieten.

Tabelle 9.7: Informationen eines Item-Events
MethodeInformation
Object getItem()Liefert das Objekt zurück, in dem das Ereignis ausgelöst wurde
int getStateChange()Liefert den Zustand des Objekts, der durch das Ereignis hergestellt wurde
getItemSelectable()Liefert einen Verweis auf das ItemSelectable-Interface der Komponente, in der das Ereignis stattgefunden hat

In den vordefinierten Komponenten liefert ein Aufruf von getItem() meist ein Objekt vom Typ String. Bei der Verwendung einer List wird ein Integer-Exemplar zurückgeliefert, das den Index des selektierten Eintrags repräsentiert. Bei eigenen Komponenten können aber auch beliebige andere Objekte zurückgegeben werden. Aus diesem Grund scheint die folgende Vorgehensweise bei der Abfrage des Eintrags am sinnvollsten: Zunächst sollte in itemStateChanged() die Komponente lokalisiert werden, in der eine Veränderung stattgefunden hat. Anschließend kann man den betroffenen Eintrag direkt über die Methoden der Komponente abfragen (z. B. mit getSelectedIndex() den aktuelle selektierten Eintrag einer List ermitteln). Das Ergebnis von getStateChange() kann über zwei Konstanten ermittelt werden, die in der Klasse ItemEvent definiert sind:

Beim Doppelklicken auf ein Listenelement in einer List gibt es eine Besonderheit:

Der erste Klick erzeugt ein Item-Event, der zweite Klick ein Action-Event.

Bei der Auswahl eines Eintrags aus einer List mit Return wird ebenfalls ein Action-Event ausgelöst. Im folgenden Beispiel werden bei einer Auswahl aus einer List jeweils die Ereignisart und das Listenelement auf der integrierten TextArea ausgegeben:
  import java.applet.Applet;
  import java.awt.*;
  import java.awt.event.*;
  
  public class ListEventDemo extends Applet implements
                           ActionListener, ItemListener {
    List myList;
    TextArea messages;
  
    public void init() {
      setLayout(new BorderLayout());
      // Erzeugen der Komponenten
      myList = new List(3, true);
      myList.add("Java");
      myList.add("Coffee");
      myList.add("Espresso");
      myList.add("Cappuccino");
      messages = new TextArea();
      // Registrierung der Listener
      myList.addActionListener(this);
      myList.addItemListener(this);
      // Hinzufügen der Komponenten
      add("North", myList);
      add("Center", messages);
    }
  
    public void actionPerformed(ActionEvent e) {
      append("Action: "+e.getActionCommand());
    }
  
    public void itemStateChanged(ItemEvent e) {
      switch(e.getStateChange()) {
      case ItemEvent.SELECTED:
        append("Item selected: "+e.getItem());
        break;
      case ItemEvent.DESELECTED:
        append("Item deselected: "+e.getItem());
      }
    }
  
    public void append(String s) {
      messages.append(s+"\n");
    }
  }

Change-Event

Das ChangeEvent ist wohl das am einfachsten aufgebaute Event bei der Oberflächenprogrammierung. Es liefert lediglich die Information über seine Quelle und wird beispielsweise vom JSlider verwendet, wenn sich der eingestellte Wert verändert hat. In diesem Fall kann der entsprechende neue Wert ausschließlich über die Komponente abgefragt werden.

  JSlider slider = new JSlider();
  slider.addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
      System.out.println("Neuer Wert: "+
        ((JSlider) e.getSource()).getValue());
    }
  });

Hyperlink-Event

Bei einem JEditorPane kann ein HyperlinkListener registriert werden, der darüber informiert, wenn der Benutzer auf einen Link klickt. Der Listener definiert dabei lediglich die Methode
public void hyperlinkUpdate(HyperlinkEvent e);
Dabei kann der übergebene Parameter ein Exemplar der Klasse HyperlinkEvent oder der Unterklasse HTMLFrameHyperlinkEvent sein. Die Unterklasse hat zusätzlich den Wert für das HTML-Attribut target, welches den Zielframe angibt.

Man unterscheidet beim HyperlinkEvent drei Typen, wobei der aktuelle Typ über die Methode getEventType() abgefragt werden kann:

Über die Methode getURL() kann die Zieladresse bezogen werden. Hierbei ist zu beachten, dass relative Links nur dann korrekt in URLs vom JEditorPane umgesetzt werden können, wenn vorher eine entsprechende URL zur aktuellen Seite mit Hilfe der Methode setPage(URL url) angegeben wurde. Wenn hingegen ein HTML-Text durch die Methode setText(String text) angegeben wurde, kann natürlich ein relativer Link dazu nicht aufgelöst werden.

In diesem Fall hilft die Methode getDescription() weiter. Sie beinhaltet den Text des HTML-Attributes href. Damit kann der Entwickler ggf. die passende URL selbst bilden.

Ein Beispiel für die Verwendung des HyperlinkEvent finden Sie in Abschnitt .

Text-Events

Ein Text-Event wird erzeugt, wenn sich der Text verändert, der in einer von TextComponent abgeleiteten Klasse dargestellt wird. Das ist z. B. der Fall, wenn der Benutzer in ein Textfeld Text eingibt. TextField und TextArea sind die einzigen vordefinierten Klassen, die solche Ereignisse auslösen können. Beim Auslösen eines Text-Events wurde der Text in einer Textkomponente bereits verändert. Um Text während der Eingabe schon zu validieren und gegebenenfalls nicht anzuzeigen, muss man auf die Behandlung von Key-Events zurückgreifen.

Zur Reaktion auf ein Text-Event muss eine Komponente das Interface TextListener implementieren. Dieses Interface enthält eine Methode: Ein Text-Event wird von der Klasse TextEvent repräsentiert. Ein Objekt dieser Klasse wird auch der Methode textValueChanged(TextEvent) als Parameter übergeben. Ein TextEvent-Objekt enthält für den Programmierer aber eigentlich nur wenige Informationen. Man hat die Möglichkeit, über die Methode getSource(), die bereits in der Klasse EventObject definiert ist, einen Verweis auf die Ereignisquelle zu erhalten. Der eigentliche Text muss dann wiederum in einem weiteren Schritt von der Ereignisquelle erfragt werden.

Text-Events stehen in einer engen Beziehung mit Key-Events. Das liegt daran, dass die Änderung des Textes normalerweise durch eine Eingabe vom Benutzer über die Tastatur vorgenommen wird. In diesem Fall werden die einzelnen Ereignisbehandlungsmethoden in der folgenden Reihenfolge aufgerufen: Dieser Sachverhalt wird durch folgendes Beispielprogramm demonstriert:
  public class TextKeyEventDemo extends Applet
               implements TextListener, KeyListener {
    TextField t;
    TextArea area;
  
    public void init() {
      setLayout(new BorderLayout());
      t = new TextField();
      t.addTextListener(this);
      t.addKeyListener(this);
      area = new TextArea();
      add("North", t);
      add("Center", area);
    }
  
    public void textValueChanged(TextEvent e) {
      append(e.toString());
    }
  
    public void keyTyped(KeyEvent e) {
      append(e.toString());
    }
  
    public void keyPressed(KeyEvent e) {
      append(e.toString());
    }
  
    public void keyReleased(KeyEvent e) {
      append(e.toString());
    }
  
    public void append(String s) {
      area.append(s+"\n");
    }
  }
Ein Text-Event wird auch dann erzeugt, wenn man innerhalb des Programms mit der Methode setText(String) den Text einer Textkomponente setzt.

Folgendes Beispiel stellt ein Java-Applet dar, das zwei Komponenten enthält: ein TextField und eine TextArea. Bei einer Änderung des Textes im TextField wird in der TextArea eine Nachricht mit dem neuen Text ausgegeben.
  import java.awt.*;
  import java.awt.event.*;
  import java.applet.Applet;
  
  public class TextEventDemo extends Applet
                               implements TextListener {
    TextArea area;
  
    public void init() {
      setLayout(new BorderLayout());
      TextField field = new TextField();
      area = new TextArea();
      field.addTextListener(this);
      add("North", field);
      add("Center", area);
    }
  
    public void textValueChanged(TextEvent e) {
      area.append("new value: "
        +((TextComponent)e.getSource()).getText()+"\n");
    }
  }
Nach der Änderung des Textes im Textfeld wird die Methode textValueChanged(TextEvent) aufgerufen. Dort wird der TextArea eine neue Zeile hinzugefügt:
  area.append("new value: "
    +((TextComponent)e.getSource()).getText()+"\n");
Zunächst wird durch Aufruf der Methode getSource() des Event-Objektes ein Verweis auf das Textfeld erfragt. Da getSource() ein Objekt vom Typ Object zurückliefert, muss zunächst eine Typumwandlung zur Klasse TextComponent durchgeführt werden, um die Methode getText() aufrufen zu können.

Document-Events

Swing-Textkomponenten (Unterklassen von JTextComponent) besitzen als Datengrundlage immer ein Exemplar der Klasse Document, bei dem man einen DocumentListener registrieren kann. Dieser wird dann über sämtliche Textänderungen informiert. Die möglichen Typen der Änderung werden mit Datenelementen der Klasse DocumentEvent dargestellt und können mit der Methode getType() aus dieser Klasse ermittelt werden: Neben dem Typ enthält der DocumentEvent Zugriff auf das zugrunde liegende Dokument (getDocument()) und die Grenzen der Textänderungen (getOffset() und getLength()).

Das folgende Beispiel aktiviert einen JButton, wenn in dem Passwortfeld ein Text länger als drei Zeichen steht. Wenn der Benutzer jedoch keinen oder maximal drei Buchstaben eingibt, bleibt der Button deaktiviert.
  continueButton = new JButton("weiter");
  continueButton.setEnabled(false);

  JPasswordField passwordField = new JPasswordField();
  passwordField.getDocument().addDocumentListener(
    new DocumentListener() {
      public void changedUpdate(DocumentEvent e) {
        test(e);
      } 
      public  void insertUpdate(DocumentEvent e) {
        test(e);
      }
      public void removeUpdate(DocumentEvent e) {
        test(e);
      }
      private void test(Document e) {
        continueButton.setEnabled(
          e.getDocument().getLength()>3);
      }
    });

Adjustment-Events

Ein Adjustment-Event stellt einen Einstellvorgang in einer Komponente dar. Eine solche Komponente sollte dem Benutzer die Möglichkeit bieten, aus einem bestimmten, diskreten Wertebereich einen Wert auszuwählen.

Adjustment-Events werden von Komponenten ausgelöst, die das Interface Adjustable implementieren, beispielsweise von Scrollbars.

Bei einem Rollbalken können Adjustment-Events auf unterschiedliche Arten erzeugt werden. Unterschieden wird hierbei die Art und Weise, auf welche die Scrollbar bewegt wird: Für jede dieser unterschiedlichen Bewegungsarten werden Ereignis-Objekte mit anderen IDs erzeugt. Somit kann man auf unterschiedliche Scroll-Ereignisse unterschiedlich reagieren. In den meisten Fällen reicht es aus, auf alle Scroll-Ereignisse gleich zu reagieren, unabhängig davon, wodurch sie ausgelöst wurden. In den folgenden Beispielen wird deshalb auch so vorgegangen. Eine Trennung ist aber prinzipiell möglich.

Komponenten, die auf einen Einstellvorgang reagieren wollen, müssen das Interface AdjustmentListener implementieren. Dieses Interface definiert eine Methode:

Tabelle 9.8: Informationen eines Adjustment-Events
MethodeInformation
int getValue()Liefert den eingestellten Wert zurück
int getAdjustmentType()Liefert einen Wert, der die Art des Einstellvorgangs anzeigt
Adjustable getAdjustable()Liefert das Adjustable-Interface der Komponente, in der das Ereignis stattgefunden hat

Für die Art des Einstellvorgangs wurden in der Klasse AdjustmentEvent jeweils unterschiedliche Konstanten definiert. Diese Konstanten werden von getAdjustmentType() zurückgeliefert. Sie sind in Tabelle 9.9 aufgeführt.

Tabelle 9.9: Arten von Einstellvorgängen
KonstanteInformation
AjustmentEvent.BLOCK_INCREMENTBlockweises Scrollen nach oben
AjustmentEvent.BLOCK_DECREMENTBlockweises Scrollen nach unten
AjustmentEvent.TRACKAbsolutes Scrollen
AjustmentEvent.UNIT_INCREMENTEinheitenweises Scrollen nach oben
AjustmentEvent.UNIT_DECREMENTEinheitenweises Scrollen nach unten

Im folgenden Beispiel wird eine Scrollbar für die Implementierung eines Reglers eingesetzt:

  public class ScrollEventDemo extends Applet
                               implements AdjustmentListener {
    Scrollbar slider;      // Scrollbar
    TextField myTextField; // Textfeld zum Anzeigen des
                           // aktuellen Wertes
    public void init() {
      setLayout(new BorderLayout());
      // Erzeugen und Initialisieren des Scrollbars
      slider = new Scrollbar(Scrollbar.HORIZONTAL);
      slider.setValues(0, 50, 0, 1050);
      // Erzeugen und Initialisieren des Textfeldes
      myTextField = new TextField();
      myTextField.setEditable(false);
      myTextField.setText("0");
      // Zufügen der Komponenten
      add("North", slider);
      add("Center", myTextField);
      slider.addAdjustmentListener(this);
    }
  
    public void adjustmentValueChanged(AdjustmentEvent e) {
      // Anzeigen des aktuellen
      // Wertes des Scrollbars im Textfeld
      myTextField.setText(String.valueOf(slider.getValue()));
    }
  }
Nach der Registrierung als Event-Listener wird bei Eintritt des Scroll-Ereignisses automatisch die Methode adjustmentValueChanged() aufgerufen. Dort wird der aktuelle Wert der Scrollbar ermittelt und im Textfeld angezeigt. Eine Unterscheidung zwischen den einzelnen Typen ist in diesem Fall nicht notwendig.

Auf der CD ist ein zweites Beispiel zu finden, das die Verwendung der Klasse Scrollbar demonstriert. Es ermöglicht das horizontale und vertikale Scrollen einer Karte, die als Bild vorliegt. Dieses Beispiel dient jedoch nur der Demonstration. Seit[1.1] JDK 1.1 kann man dieselbe Funktionalität mit Hilfe der Klasse ScrollPane sehr viel einfacher bereitstellen.

Fokus-Events

Fokus-Events definieren eine weitere Gruppe von Ereignissen, die innerhalb der grafischen Oberfläche auftreten können. »Den Fokus haben« bedeutet für eine Komponente, Tastatureingaben übermittelt zu bekommen. Wenn eine grafische Oberfläche viele Komponenten besitzt, ist es möglich, dass mehrere Komponenten auf Tastatureingaben reagieren können. Dies ist z. B. der Fall, wenn eine Oberfläche zwei Textfelder enthält. Aber nur diejenige der Komponenten, die den Fokus gerade besitzt, erhält die Eingaben von der Tastatur übermittelt. Die Komponente, die gerade im Besitz des Fokus ist, wird optisch ein wenig anders dargestellt, meist eingerahmt. Jede von der Klasse Component abgeleitete Klasse kann prinzipiell Fokus-Events erzeugen (das sind alle Komponenten, die in der Lage sind, den Fokus zu erhalten).

Eine Komponente, die auf Fokus-Events reagieren möchte, muss das Interface FocusListener implementieren. Dieses Interface definiert folgende Methoden:

Ein Fokus-Event wird durch die Klasse FocusEvent repräsentiert. Diese Klasse definiert nur eine für den Programmierer wichtige Methode: Bei bestimmten Aktionen ist eine Fokusänderung nur temporär. Das ist dann der Fall, wenn der Fokus wegen einer anderen kurzzeitigen Tätigkeit gewechselt werden muss. Beispiele für solche temporären Fokuswechsel sind z. B. das Bedienen einer Scrollbar oder das Wechseln des aktuellen Fensters in der grafischen Oberfläche. In derartigen Situationen werden die Fokusinformationen vor dem temporären Wechsel des Fokus gespeichert und nach Beendigung der temporären Aktion wieder hergestellt, so dass der alte Zustand wieder vorhanden ist. Damit besitzt nach der Reaktivierung eines Fensters wieder die Komponente in diesem Fenster den Fokus, die ihn bereits schon vor dem Wechsel hatte.

Ein permanenter Wechsel des Fokus wird hingegen entweder über die Methoden requestFocus(), transferFocus() oder durch die Ansteuerung von Komponenten über die Tab-Taste bzw. durch Auswahl mit der Maus vorgenommen. isTemporary() zeigt an, ob es sich um einen temporären oder einen permanenten Fokuswechsel handelt.

Wie oben schon angesprochen, kann man mit der Tab-Taste den Fokus zur nächsten Komponente bewegen. Durch gleichzeitiges Drücken von Shift und Tab wird der Fokus zur vorherigen Komponente bewegt. Die Reihenfolge, in welcher die einzelnen Komponenten beim Drücken von Tab den Fokus erhalten (»Taborder«), hängt davon ab, in welcher Reihenfolge die Komponenten der grafischen Oberfläche hinzugefügt wurden. Wird eine Komponente ohne weiteren Parameter durch add() der grafischen Obefläche hinzugefügt, so wird sie in der Taborder hinter die schon vorhandenen Komponenten eingereiht. Es ist aber auch möglich, add() explizit die Position in der Taborder zu übergeben.
  Button b = new Button(click);
  add(b, 3);
Jede Komponente besitzt die Methode Diese Methode liefert einen booleschen Wert, der anzeigt, ob die Komponente in der Lage ist, den Fokus zu erhalten. Bei einem Canvas liefert diese Methode per Voreinstellung den Wert false. Wenn man nun von Canvas eine eigene Klasse ableitet, die in der Lage sein soll, den Fokus zu erhalten, muss isFocusTraversable() so überschrieben werden, dass true zurückgeliefert wird. Bei Komponenten, die deaktiviert werden können, ist es vom aktuellen Zustand abhängig, ob die Komponente in der Lage ist, den Fokus zu bekommen. Eine deaktiverte Komponente sollte nicht in der Lage sein, den Fokus zu erhalten.

Die Weiterleitung des Fokus wird vom Fokus-Manager übernommen. Jedes Fenster besitzt einen solchen Fokus-Manager. Bei einem Fokuswechsel überprüft der Fokus-Manager, ob die Komponente, die den Fokus erhalten soll, gerade sichtbar ist und deren Methode isFocusTraversable() true als Ergebnis liefert. Ist beides der Fall, so erhält die betreffende Komponente den Fokus.

InputVerifier

Jede Swing-Komponente kann verhindern, dass sie den permanenten Fokus verliert. Zu diesem Zweck gibt es die abstrakte Klasse javax.swing.InputVerifier, welche über die JComponent-Methode setInputVerifier() gesetzt werden kann. Kern eines InputVerifier ist die Methode
  public boolean verify(JComponent comp)
Diese Methode bekommt als Parameter die Komponente übergeben, die den Fokus verlieren soll. Liefert die Methode true zurück, so lautet die Aussage, dass die Komponente in einem korrekten Zustand für den Wechsel des Fokus vorliegt. Die zweite Methode public boolean shouldYieldFocus(JComponent input) ruft in der Standardimplementierung die verify-Methode direkt auf. Der Unterschied ist eher in der Aussage der Methode zu suchen: Während die erste Methode über den Status der Komponente eine Aussage liefert, sagt die zweite Methode, ob der Fokus gewechselt werden darf - normalerweise über die Aussage, ob die Komponente in einem korrekten Status ist.

Window-Events

Jedes Fenster, das auf dem Bildschirm angezeigt wird, kann auf bestimmte Ereignisse reagieren.

Stellt man mit der Methode setDefaultCloseOperation(int) bei einem JFrame die Konstante DO_NOTHING_ON_CLOSE aus der Klasse WindowConstants ein, wird man feststellen, dass das Fenster nun nicht mehr über den Desktop geschlossen werden kann. Dies liegt daran, dass der JFrame nicht auf ein WINDOW_CLOSING-Ereignis reagiert. Durch die Reaktion auf ein WINDOW_CLOSING-Ereignis ist es möglich, Tätigkeiten auszuführen, bevor das Fenster geschlossen wird, wie z. B. das Speichern von Daten. Besitzt eine Applikation eine grafische Oberfläche, ist es ebenfalls denkbar, die Applikation beim Schließen des Fensters durch Aufruf von System.exit() zu beenden.

Soll das Schließen des Fensters funktionieren, muss das Interface WindowListener implementiert werden. Dort sind folgende Methoden definiert: Dummy-Implementierungen für diese Methoden sind in der Klasse WindowAdapter enthalten. Für das Schließen eines Fensters bei Auswahl aus dem Fenster-Menü ist nur die Methode windowClosing(WindowEvent) relevant. Nach der Registrierung eines Window-Listeners wird diese Methode aufgerufen, wenn ein Versuch unternommen wird, ein Fenster zu schließen, es aber noch am Bildschirm zu sehen ist. Das ist bei der Auswahl des Menüpunktes »Schließen« aus dem Fenster-Menü der Fall. Das Fenster wird durch die Auswahl des Menüpunktes aber noch nicht vom Bildschirm entfernt. Diese Aufgabe bleibt dem Programmierer überlassen. Ein Aufruf von windowClosing(WindowEvent) erfolgt allerdings nicht, wenn das Fenster durch einen Aufruf von dispose() innerhalb des Programms vom Bildschirm entfernt wird. Hierdurch wird nur windowClosed(WindowEvent) aufgerufen. Wenn man nun innerhalb von windowClosing(WindowEvent) die Methode dispose() des Fensters aufruft, so wird bei Auswahl des »Schließen«-Menüpunktes das Fenster vom Bildschirm entfernt und dadurch wiederum windowClosed(WindowEvent) des Window-Listeners aufgerufen.

Im folgenden Beispiel wird die Anwendung beim Schließen des Fensters beendet:
  import java.awt.*;
  import java.awt.event.*;
  
  public class WindowEventDemo extends Frame
                               implements ActionListener {
  
    public static void main(String args[]) {
      WindowEventDemo frame =
                  new WindowEventDemo("WindowEventDemo");
      frame.setSize(200, 200);
      frame.show();
    }
  
    public WindowEventDemo(String name) {
      super(name);  // Setzen des Titels
      Button b = new Button("Exit");
      add("Center", b);
      b.addActionListener(this);
      addWindowListener(new MyWindowAdapter(this));
    }
  
    public void actionPerformed(ActionEvent e) {
      System.out.println(e);
      dispose();
    }
  
    public void close() {
      dispose();
    }
  }
  
  class MyWindowAdapter extends WindowAdapter {
    protected WindowEventDemo demo;
  
    public MyWindowAdapter(WindowEventDemo demo) {
      this.demo = demo;
    }
  
    public void windowClosing(WindowEvent e) {
      System.out.println(e);
      demo.close();
    }
  
    public void windowClosed(WindowEvent e) {
      System.out.println(e);
      System.exit(0);
    }
  }
Im Beispiel wird eine Adapterklasse MyWindowAdapter verwendet, in der auf die Aufforderung zum Schließen des Fensters reagiert wird. In diesem Fall wird die Methode close() der Fensterklasse aufgerufen, in der das Fenster vom Bildschirm entfernt und das Programm beendet wird.

Component- und Container-Events

Component- und Container-Events definieren Ereignisse, die prinzipiell in jeder Komponente bzw. jedem Container ausgelöst werden können. Sie werden allerdings nicht direkt durch Interaktion mit dem Benutzer eines Programms hervorgerufen. Component-Events können dazu verwendet werden, Objekte über die Veränderung der Lage einer Komponente zu informieren. Ein Objekt, das diese Fähigkeit besitzen soll, muss das Interface ComponentListener implementieren. Dort sind folgende Methoden definiert: Eine entsprechende Adapter-Klasse ComponentAdapter ist ebenfalls im Paket java.awt.event enthalten.

Bei Containern können sich Objekte registrieren, die an Informationen über das Hinzufügen oder Entfernen von Komponenten aus diesem Container interessiert sind. Diese Objekte müssen das Interface ContainerListener implementieren, das folgende Methoden enthält: Für die Abarbeitung von Container-Events kann man auch die Adapter-Klasse ContainerAdapter verwenden.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.