Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 15 Komponenten, Container und Ereignisse
  gp 15.1 Es tut sich was – Ereignisse beim AWT
    gp 15.1.1 Was ist ein Ereignis?
    gp 15.1.2 Die Klasse AWTEvent
    gp 15.1.3 Events auf verschiedenen Ebenen
    gp 15.1.4 Ereignisquellen, -senken und Horcher (Listener)
    gp 15.1.5 Listener implementieren
    gp 15.1.6 Listener bei Ereignisauslöser anmelden/abmelden
    gp 15.1.7 Aufrufen der Listener
  gp 15.2 Varianten, das Fenster zu schließen
    gp 15.2.1 Eine Klasse implementiert die Schnittstelle WindowListener
    gp 15.2.2 Adapterklassen nutzen
    gp 15.2.3 Innere Mitgliedsklassen und innere anonyme Klassen
    gp 15.2.4 Generic Listener
  gp 15.3 Komponenten im AWT und in Swing
    gp 15.3.1 Peer-Klassen und Lightweight-Komponenten
    gp 15.3.2 Die Basis aller Komponenten: Component und JComponent
    gp 15.3.3 Proportionales Vergrößern eines Fensters
    gp 15.3.4 Dynamisches Layout während einer Größenänderung
    gp 15.3.5 Hinzufügen von Komponenten
  gp 15.4 Das Swing-Fenster JFrame
    gp 15.4.1 Kinder auf einem Swing-Fenster
    gp 15.4.2 Schließen eines Swing-Fensters
    gp 15.4.3 JWindow und JDialog
  gp 15.5 Informationstext über die Klasse JLabel
    gp 15.5.1 Mehrzeiliger Text, HTML in der Darstellung
  gp 15.6 Die Klasse ImageIcon
    gp 15.6.1 Die Schnittstelle Icon
    gp 15.6.2 Was Icon und Image verbindet
  gp 15.7 Eine Schaltfläche (JButton)
    gp 15.7.1 Der aufmerksame ActionListener
    gp 15.7.2 Generic Listener für Schaltflächen-Ereignisse verwenden
    gp 15.7.3 AbstractButton
    gp 15.7.4 JToggleButton
  gp 15.8 Tooltips
  gp 15.9 Der Container JPanel
  gp 15.10 Alles Auslegungssache: die Layoutmanager
    gp 15.10.1 FlowLayout
    gp 15.10.2 BorderLayout
    gp 15.10.3 GridLayout
    gp 15.10.4 Der GridBagLayout-Manager
    gp 15.10.5 Null-Layout
    gp 15.10.6 Weitere Layoutmanager
  gp 15.11 Horizontale und vertikale Schieberegler
    gp 15.11.1 Der AdjustmentListener, der auf Änderungen hört
  gp 15.12 JSlider
  gp 15.13 Ein Auswahlmenü – Choice, JComboBox
    gp 15.13.1 ItemListener
    gp 15.13.2 Zuordnung einer Taste mit einem Eintrag
    gp 15.13.3 DateComboBox
  gp 15.14 Eines aus vielen – Kontrollfelder (JCheckBox)
    gp 15.14.1 Ereignisse über ItemListener
  gp 15.15 Kontrollfeldgruppen, Optionsfelder und JRadioButton
  gp 15.16 Der Fortschrittsbalken JProgressBar
  gp 15.17 Rahmen (Borders)
  gp 15.18 Symbolleisten alias Toolbars
  gp 15.19 Menüs
    gp 15.19.1 Die Menüleisten und die Einträge
    gp 15.19.2 Menüeinträge definieren
    gp 15.19.3 Mnemonics und Shortcuts (Accelerator)
    gp 15.19.4 Beispiel für ein Programm mit Menüleisten
    gp 15.19.5 Popup-Menüs
  gp 15.20 Das Konzept des Model-View-Controllers
  gp 15.21 List-Boxen
  gp 15.22 JSpinner
  gp 15.23 Texteingabefelder
    gp 15.23.1 Text in einer Eingabezeile
    gp 15.23.2 Die Oberklasse der JText-Komponenten: JTextComponent
    gp 15.23.3 JPasswordField
    gp 15.23.4 Validierende Eingabefelder
    gp 15.23.5 Mehrzeilige Textfelder
    gp 15.23.6 Die Editor-Klasse JEditorPane
  gp 15.24 Bäume mit JTree-Objekten
    gp 15.24.1 Selektionen bemerken
  gp 15.25 Tabellen mit JTable
    gp 15.25.1 Ein eigenes Tabellen-Model
    gp 15.25.2 AbstractTableModel
    gp 15.25.3 DefaultTableModel
    gp 15.25.4 Ein eigener Renderer für Tabellen
    gp 15.25.5 Zell-Editoren
    gp 15.25.6 Größe und Umrandung der Zellen
    gp 15.25.7 Spalteninformationen
    gp 15.25.8 Tabellenkopf von Swing-Tabellen
    gp 15.25.9 Selektionen einer Tabelle
    gp 15.25.10 Ein professionelles Tabellenlayout mit JGrid
  gp 15.26 JRootPane, JLayeredPane und JDesktopPane
    gp 15.26.1 JRootPane und JLayeredPane
    gp 15.26.2 JDesktopPane und die Kinder JInternalFrame
    gp 15.26.3 Der Farbauswahldialog JColorChooser
    gp 15.26.4 Der Dateiauswahldialog
  gp 15.27 Flexibles Java-Look&Feel
  gp 15.28 Swing-Beschriftungen einer anderen Sprache geben
  gp 15.29 Die Zwischenablage (Clipboard)
  gp 15.30 Undo durchführen
  gp 15.31 Ereignisverarbeitung auf unterster Ebene
  gp 15.32 AWT, Swing und die Threads
    gp 15.32.1 Warum Swing nicht Thread-sicher ist
    gp 15.32.2 Swing-Elemente bedienen mit invokeLater() und invokeAndWait()
  gp 15.33 Selbst definierte Cursor
    gp 15.33.1 Flackern des Mauszeigers bei Animationen vermeiden
  gp 15.34 Mausrad-Unterstützung
  gp 15.35 Benutzerinteraktionen automatisieren
    gp 15.35.1 Automatisch in die Tasten hauen
    gp 15.35.2 Mausoperationen
    gp 15.35.3 Methoden zur Zeitsteuerung
    gp 15.35.4 Screenshots
    gp 15.35.5 Funktionsweise und Beschränkungen
    gp 15.35.6 Zeitliches Ausführen mit dem javax.swing.Timer
    gp 15.35.7 MouseInfo und PointerInfo
  gp 15.36 Alternativen zu AWT und Swing
    gp 15.36.1 XML-Beschreibungen der Oberfläche: Swixml, XUL/Luxor
    gp 15.36.2 SWT


Galileo Computing

15.7 Eine Schaltfläche (JButton)  downtop

Eine Schaltfläche (engl. button) wird verwendet, wenn der Anwender eine Aktion auslösen soll. Schaltflächen sind meistens beschriftet und stellen eine Zeichenkette dar. Unter dem AWT stellt die Klasse Button Schaltflächen dar. Erst mit der Schaltfläche JButton unter Swing lässt sich auch ein Icon mit auf die Schaltfläche bringen. Zudem ist die Hintergrundfarbe beim Standard-Look&Feel eine etwas andere, denn sie besitzt die Hintergrundfarbe des Containers.

Die Schaltfläche kann reagieren, wenn sie gedrückt wird. Dann erzeugt sie ein ActionEvent, das über einen ActionListener abgefangen wird.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 15.5   JButton mit einer einfachen Schaltfläche zum Schließen

Listing 15.9   JButtonDemo.java


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

public class JButtonDemo
{
  public static void main( String args[] )
  {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    JButton b = new JButton( "Ende" );
    frame.getContentPane().add( b );

    ActionListener al = new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        System.exit( 0 );
      }
    };

    b.addActionListener( al );

    frame.pack();
    frame.setVisible( true );
  }
}

Es gibt mehrere Konstruktoren für JButton-Objekte. Die parameterlose Variante erzeugt eine Schaltfläche ohne Text. Der Text lässt sich aber mit setText() nachträglich ändern. In der Regel nutzen wir den Konstruktor, dem ein String mitgegeben wird.



class javax.swing.  JButton  
extends AbstractButton
implements Accessible

gp  JButton()
Erzeugt eine neue Schaltfläche ohne Aufschrift.
gp  JButton( String text )
Erzeugt eine neue Schaltfläche mit Aufschrift.
gp  JButton( Icon icon )
Erzeugt eine neue Schaltfläche mit Icon.
gp  JButton( String text, Icon icon )
Erzeugt eine neue Schaltfläche mit Aufschrift und Icon.
gp  void setText( String text )
Ändert die Aufschrift der Schaltfläche im laufenden Betrieb.
gp  String getText()
Ändert die Aufschrift der Schaltfläche im laufenden Betrieb.
gp  void addActionListener( ActionListener l )
Fügt dem Button einen ActionListener hinzu, der die Ereignisse, die durch die Schaltfläche ausgelöst werden, abgreift.
gp  void removeActionListener( ActionListener l )
Entfernt den ActionListener wieder. Somit kann er keine weiteren Ereignisse mehr abgreifen.

Wie wir schon gesehen haben, lässt sich der Text eines JButton-Objekts nachträglich mit setText(String) setzen und mit der entsprechenden Methode getText() auslesen. Vergleichen wir das mit den Methoden der Klasse Label. Hier heißen die Methoden zum Lesen und Ändern des Textes setLabel() und getLabel(). Dies ist für mich wieder eines der AWT-Rätsel. Warum heißt es bei einem Label-Objekt xxxText() und bei einem Button-Objekt xxxLabel()?


Hinweis   Wörter mit einer starken emotionalen Bindung sollten vermieden werden. In englischen Programmen müssen Wörter wie »kill« oder »abort« umgangen werden.


Galileo Computing

15.7.1 Der aufmerksame ActionListenedowntop

Drücken wir die Schaltfläche, so sollte die Aktion gemeldet werden. Diese wird in Form eines ActionEvent-Objekts an den Zuhörer (ein ActionListener) gesandt. Ein ActionListener wird mit der Methode addActionListener() an die Objekte angeheftet, die Aktionen auslösen können. ActionListener ist eine Schnittstelle mit der Methode actionPerformed(). ActionListener erweitert die Schnittstelle EventListener, die von allen Listener-Interfaces implementiert werden muss.



interface java.awt.event.  ActionListener  
extends EventListener

gp  abstract void actionPerformed( ActionEvent e )
Wird aufgerufen, wenn eine Aktion ausgelöst wird.

Werfen wir noch einmal einen Blick auf die Implementierung unserer ActionListener-Klasse, die als anonyme innere Klasse realisiert ist:


ActionListener al = new ActionListener()
{
    public void actionPerformed( ActionEvent e )  
  {
    System.exit( 0 );
  }
}

Wird ein ActionEvent ausgelöst, dann wird die Methode actionPerformed() aufgerufen. Wir sehen, dass als Argument ein ActionEvent übergeben wird; die Klasse besitzt drei Methoden:



class java.awt.event.  ActionEvent  
extends AWTEvent

gp  String getActionCommand()
Liefert den String, der mit dieser Aktion verbunden ist. Damit lassen sich Komponenten unterscheiden. Innerhalb der Button-Objekte handelt es sich bei dem Action-Command um die Aufschrift der Schaltfläche. Mit wenig Aufwand lässt sich so eine Schaltfläche erzeugen, die zwei Zustände beinhalten kann, beispielsweise mehr oder weniger Details. Der Behandler für das ActionEvent kann so der gleiche bleiben, kann aber über die Zeichenkette den Zustand lesen. Leider verfangen wir uns bei dieser Programmmethode schnell in landessprachenabhängiger Software. Wir werden im nächsten Abschnitt sehen, dass es dazu sinnvoll ist, den Action-Command zu setzen.
gp  int getModifiers()
Liefert den Umschaltcode für Tasten, die während des Ereignisses gedrückt waren. Die Masken sind selbstsprechend. Folgende sind als Konstanten definiert: SHIFT_MASK, CTRL_MASK, META_MASK, ALT_MASK. Diese Konstanten kommen direkt aus der Event-Klasse, so ist etwa ALT_MASK = Event.ALT_MASK. Zusätzlich gibt es noch weitere Masken: ACTION_FIRST, ACTION_LAST und ACTION_PERFORMED, die ein Synonym für ACTION_FIRST ist.
gp  String paramString()
Liefert den Erkennungsstring für das Ereignis. paramString() überschreibt die Methode von der abstrakten Klasse java.awt.AWTEvent, die dort nur den Leerstring zurückgibt, und implementiert gemäß der Vorgabe eine Methode, die einen String liefert, der den Typ der Operation und getActionCommand() vereint. Der String beginnt entweder mit »ACTION_PERFORMED« oder mit »unknown type«.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Den Erkennungsstring ändern

Wir haben gesehen, dass wir mit der getActionCommand()-Methode aus ActionEvent einen Kommandostring erfragen können, der im Fall der Schaltfläche mit der Beschriftung übereinstimmt. Dass allerdings dieser Text der Aufschrift entspricht, kann einschränkend und problematisch sein, zum Beispiel dann, wenn sich wie bei mehrsprachigen Anwendungen die Aufschrift ändert. Als Lösung bietet die JButton-Klasse die Methode setActionCommand() an, mit der dieser String geändert werden kann.



abstract class javax.swing.  AbstractButton  
extends JComponent
implements ItemSelectable, SwingConstants

gp  void setActionCommand( String command )
Setzt einen neuen Kommandostring, wenn das Ereignis ausgeführt wird. So empfängt getActionCommand() dann command.

Galileo Computing

15.7.2 Generic Listener für Schaltflächen-Ereignisse verwendedowntop

Die Idee bei den Generic Listenern war, einen Ereignisauslöser mit einer Methode zu verbinden, die automatisch mittels Reflection aufgerufen wird. Damit das für die Ereignisbehandlung funktioniert, muss der Aufrufer natürlich die Methode finden und die Ereignisse zustellen können. Wir zeigen zunächst an einem Beispiel, wie an eine Schaltfläche button eine ActionListener gebunden wird. Dafür definieren wir erst eine Methode, die im Fall eines Ereignisses aufgerufen werden soll. Sie trägt nicht den typischen Methodennamen actionPerformed, nimmt aber genauso als Parameter ein ActionEvent-Objekt entgegen. Der Parameter darf nicht fehlen.


public void gedrückt( ActionEvent e ) {
 System.out.println( "Button über Proxy" );
}

Als Nächstes müssen wir ein ActionListener-Objekt konstruieren und mittels der Methode addActionListener() der Schaltfläche hinzufügen. Den ActionListener konstruieren wir mit der statischen Funktion create() vom GenericListener.


ActionListener buttonActionListener =
  (ActionListener)(GenericListener.create(
     ActionListener.class,
     "actionPerformed",
     this,
     "gedrückt") );
button.addActionListener( buttonActionListener );

Die Parameter von create() sind:

gp  Die Schnittstelle, von der das temporäre Event-Handler abgeleitet ist
gp  Die Methode der Schnittstelle, die in Wirklichkeit aufgerufen wird
gp  Das Zielobjekt (bei this sind wir das)
gp  Die Zielmethode, also die Methode, die wir selbst implementieren und die im Fall des Ereignisses aufgerufen wird

Beispiel   Wir wollen ein Programm implementieren, das zwei Schaltflächen anordnet. Die eine Schaltfläche soll über einen herkömmlichen Mechanismus die Ereignisse verwalten, und das über eine innere anonyme Klasse. Die zweite Schaltfläche soll den neuen Mechanismus mit GenericListener einsetzen. Dann soll dem Frame noch eine Methode gegeben werden, damit das Fenster geschlossen wird.

Interessant ist bei den Generic Listenern, dass die Auswahl einer Methode auch dann funktioniert, wenn die Schnittstelle mehrere Methoden vorschreibt. Das gilt etwa bei einem WindowListener. Wir können uns dennoch nur für eine Methode entscheiden.


WindowListener winListener =
    (WindowListener)(GenericListener.create(
                       WindowListener.class,
                       "windowClosing",
                       this,
                       "fensterWirdGeschlossen") );

Obwohl der WindowListener viele Methoden vorschreibt, leiten wir lediglich bei einem windowClosing an unsere eigene Methode fensterWirdGeschlossen weiter. Und so sieht das gesamte Programm aus:

Listing 15.10   GenericListenerDemo


package genericlistener;

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


public class GenericListenerDemo extends JFrame
{
  static ActionListener proxyActionListener( JComponent target,
                                             Object implementor,
                                             String methodname )
  {
    return (ActionListener)(GenericListener.create(
                              ActionListener.class,
                              "actionPerformed",
                              implementor,
                              methodname));
  }

  GenericListenerDemo()
  {
    JButton button1 = new JButton( "Normaler Listener" );
    JButton button2 = new JButton( "Dynamischer Listener" );

    getContentPane().setLayout( new GridLayout(2,0) );
    getContentPane().add( button1 );
    getContentPane().add( button2 );

    // Listener wie bekannt hinzufügen

    button1.addActionListener( new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            button1Action(e);
          }
        } );


    // Die andere Variante

    button2.addActionListener( proxyActionListener( button2, this, 
"button2Action" ) );

    // windowClosing() ist nur eine Methode von vielen

    WindowListener winListener =
      (WindowListener)(GenericListener.create(
                         WindowListener.class,
                         "windowClosing",
                         this,
                         "fensterWirdGeschlossen"));

    addWindowListener( winListener );

    pack();
    setVisible( true );
  }

  public void button1Action( ActionEvent e ) {
    System.out.println( "Buttton 1" );
  }

  public void button2Action( ActionEvent e ) {
    System.out.println( "Button 2 über Proxy" );
  }

  public void fensterWirdGeschlossen( WindowEvent e ) {
    System.exit( 0 );
  }

  public static void main( String args[] )
  {
    new GenericListenerDemo();
  }
}

Galileo Computing

15.7.3 AbstractButton  downtop

Die direkte Oberklasse für JButton ist AbstractButton, die auch für JMenuItem (somit für JCheckBox und JRadioButton) und JToggleButton Implementierungen vorgibt. Der AbstractButton ist, wie der Name schon sagt, eine abstrakte Klasse, die aus JComponent hervorgeht. Über die Oberklasse lassen sich folgende Eigenschaften für Schaltflächen steuern:

gp  Ein Mnemonik; ein Zeichen, welches im Text unterstrichen dargestellt wird und schnell über Alt+Taste aufgerufen werden kann. Dies übernimmt die Methode setMnemonic (char).
gp  Automatisch eine Aktion auslösen durch doClick().
gp  Den Zustand des Icons aufgrund des Status mit setDisabledIcon(), setDisabledSelectedIcon(), setPressedIcon(), setRolloverIcon(), setRolloverSelectedIcon() und setSelectedIcon() ändern. Alle Methoden haben ein Icon-Objekt als Parameter.
gp  Die Ausrichtung von Text und Icon in der Schaltfläche durch setVerticalAlignment() und setHorizontalAlignment() bestimmen.
gp  Die Position von Icon und Text untereinander durch setVerticalTextPosition() und setHorizontalTextPosition() bestimmen.

Die Integration mit den Icon-Objekten liegt in der AbstractButton-Klasse. Geben wir im Konstruktor das Icon nicht an, so lässt sich dies immer noch über setIcon() nachträglich setzen und ändern. Wenn die Schaltfläche gedrückt wird, kann ein anderes Bild erscheinen. Dieses Icon setzt setPressedIcon(). Bewegen wir uns über die Schaltfläche, lässt sich auch ein anderes Icon setzen. Dazu dient die Methode setRolloverIcon(). Die Fähigkeit muss aber erst mit setRolloverEnabled(true) eingeschaltet werden. Beide Eigenschaften lassen sich auch zu einem Icon kombinieren, das erscheint, wenn die Maus über dem Bild ist und eine Selektion gemacht wird. Dazu dient setRolloverSelectedIcon(). Für ToggleButton-Objekte ist eine weitere Methode wichtig, denn ein ToggleButton hat zwei Zustände: einen selektierten und einen nicht selektierten. Auch hier können zwei Icon-Objekte zugeordnet werden, und das Icon der Selektion lässt sich mit setSelectedIcon() setzen. Ist die Schaltfläche ausgegraut, ist auch hier ein extra Icon möglich. Es wird mit setDisabledIcon() gesetzt. Dazu passt setDisabledSelectedIcon().

Abbildung
Hier klicken, um das Bild zu Vergrößern

Flache Schaltflächen

Neuerdings werden Schaltflächen immer flach gezeichnet, also ohne Rahmen. Auch dies können wir in Java einrichten, und es wirkt bei Symbolleisten mit Grafiken noch zeitgemäßer. Folgende Implementierung bietet sich an:

Listing 15.11   JIconButton.java


public class JIconButton extends JButton
{
  public JIconButton( String file )
  {
    super( new ImageIcon(file) );
    setContentAreaFilled( false );
    setBorderPainted( false );
    setFocusPainted( false );
  }
}

Galileo Computing

15.7.4 JToggleButton  toptop

Ein JToggleButton (zu Deutsch Wechselknopf) hat im Gegensatz zum JButton zwei Zustände. Dies ist vergleichbar mit einem Schalter, der den Zustand »Ein« oder »Aus« annimmt. Der JButton gerät in diesen Zustand nur bei der Aktivierung, springt dann aber wieder in seinen ursprünglichen Zustand zurück. Der JToggleButton springt bei der ersten Aktivierung in einen festen Zustand und bleibt dort solange, bis er wieder aktiviert wird. Im alten AWT hat er keine Entsprechung und wird auch unter Swing selten verwendet. Er dient jedoch als Oberklasse für die Auswahlknöpfe JCheckBox und JRadioButton.





Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de