Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 36 - Swing: Container und Menüs

## 36.2 Menüs

- 36.2 Menüs
  - 36.2.1 Einfache Menüs
  - 36.2.2 Grundlagen von Swing-Menüs
    - JMenuBar
    - JMenu
    - JMenuItem
  - 36.2.3 Weitere Möglichkeiten
    - Untermenüs
    - Icons in Menüeinträgen
    - Checkboxes und Radiobuttons in Menüeinträgen
  - 36.2.4 Kontextmenüs

### 36.2.1 Einfache Menüs

### 36.2.2 Grundlagen von Swing-Menüs

#### JMenuBar

In Swing können alle Hauptfenster mit Ausnahme von `JWindow` eine Menüleiste haben. Dabei handelt es sich um eine Instanz der Klasse `JMenuBar`, die dem Hauptfenster durch Aufruf von `addJMenuBar` hinzugefügt wird. `JMenuBar` besitzt eine Vielzahl von Methoden, meist werden aber lediglich der parameterlose Konstruktor und die Methode `add` benötigt.

    public JMenuBar()

    public JMenu add(JMenu c)
| javax.swing.JMenuBar

|  | Swing-Menüs und ihre Bestandteile haben starke Ähnlichkeit mit den korrespondierenden Konzepten im AWT. Viele der in Kapitel 30 erläuterten Eigenschaften gelten damit analog für Swing-Menüs. |  |
Hinweis

#### JMenu

Die einzelnen Menüs einer Menüleiste sind Instanzen der Klasse `JMenu`, die aus `JMenuItem` abgeleitet ist. Ihre wichtigsten Methoden sind:

    public JMenu(String s)

    public JMenuItem add(String s)
    public JMenuItem add(JMenuItem menuItem)

    public void addSeparator()
| javax.swing.JMenu

Der Konstruktor erzeugt ein neues Menü mit dem angegebenen Namen. Mit `add` werden Menüeinträge hinzugefügt. Im allgemeinen Fall sind das Instanzen der Klasse `JMenuItem`, die auf vielfältige Weise konfiguriert werden können. Es ist aber auch möglich, lediglich Strings an `add` zu übergeben. Dadurch wird ein einfacher Menüeintrag dieses Namens angefügt. Mit `addSeparator` wird eine Trennlinie hinter dem letzten Menüpunkt angefügt.

|  | Für OO-Neulinge ist es schwer zu verstehen, warum ein Menü die Ableitung eines Menüeintrags ist. Eine einfache Erklärung ist, daß es möglich sein soll, Menüs ineinander zu schachteln. Daß also ein Element eines Menüs, das ja per Definition ein Menüeintrag ist, wahlweise auch ein Menü enthalten soll. Das kann nur erreicht werden, wenn Menü und Menüeintrag zuweisungskompatibel, also voneinander abgeleitet, sind. Eine ausführlichere Erklärung findet sich bei der Besprechung des Composite-Patterns in Abschnitt 10.3.7. |  |
Hinweis

#### JMenuItem

Die Klasse `JMenuItem` repräsentiert Menüeinträge, also Elemente, die sich in einem Menü befinden. Dabei handelt es sich um Texte, die wahlweise mit einem Icon oder einem Häkchen versehen werden können. Da `JMenu` aus `JMenuItem` abgeleitet ist, kann ein Menü wiederum Menüs als Einträge enthalten. Auf diese Weise lassen sich Menüs schachteln. Die wichtigsten Eigenschaften von `JMenuItem` sind:

    public JMenuItem(String text)
    public JMenuItem(String text, Icon icon)
    public JMenuItem(String text, int mnemonic)

    public void setMnemonic(char mnemonic)
    public void setMnemonic(int mnemonic)
    public int getMnemonic()

    public void setAccelerator(KeyStroke keyStroke)
    public KeyStroke getAccelerator()

    public void setEnabled(boolean b)
    public boolean isEnabled()

    public void addActionListener(ActionListener l)
    public void removeActionListener(ActionListener l)
| javax.swing.JMenuItem

Der an den Konstruktor übergebene String `text` legt den Menütext fest. Wahlweise kann zusätzlich ein Icon übergeben werden, das neben dem Menütext angezeigt wird. Mit `setMnemonic` wird das mnemonische Kürzel des Menüeintrags festgelegt. Das ist ein (unterstrichen dargestellter) Buchstabe innerhalb des Menütexts, der bei geöffnetem Menü gedrückt werden kann, um den Menüeintrag per Tastatur aufzurufen. Wahlweise kann das Kürzel auch schon an den Konstruktor übergeben werden. Mit `getMnemonic` kann es abgefragt werden.

Neben den mnemonischen Kürzeln gibt es eine weitere Möglichkeit, Menüeinträge über Tastenkürzel aufzurufen. Diese als Acceleratoren oder Beschleunigertasten bezeichneten Tasten können auch dann verwendet werden, wenn das entsprechende Menü nicht geöffnet ist. Beschleuniger werden mit `setAccelerator` zugewiesen. Sie bestehen aus einer Kombination von normalen Tasten und Umschalttasten, die durch ein `KeyStroke`-Objekt beschrieben werden.

Instanzen der Klasse `KeyStroke` werden ausschließlich mit ihrer Factory-Methode `getKeyStroke` erzeugt:

    public static KeyStroke getKeyStroke(char keyChar)
    public static KeyStroke getKeyStroke(int keyCode, int modifiers)
| javax.swing.KeyStroke

In der einfachsten Form wird lediglich ein einzelnes Zeichen übergeben. In diesem Fall repräsentiert der erzeugte `KeyStroke` genau die entsprechende Taste. Soll diese zusammen mit Umschalttasten gedrückt werden, muß die gewünschte Kombination an den Parameter `modifiers` übergeben werden:

Konstante | Bedeutung
SHIFT_MASK | `[UMSCHALT]`
CTRL_MASK | `[STRG]`
META_MASK | `[META]` (gibt es auf den meisten Plattformen nicht)
ALT_MASK | `[ALT]`

Tabelle 36.2: Konstanten für Umschalttasten

Diese Konstanten stammen aus der Klasse `java.awt.Event`. Sie werden durch unterschiedliche Zweierpotenzen repräsentiert und können durch Addition beliebig kombiniert werden. Als erstes Argument `keyCode` ist einer der in `java.awt.event.KeyEvent` definierten virtuellen Tastencodes zu übergeben (siehe Tabelle 29.4).

Mit den Methoden `setEnabled` und `getEnabled` kann auf den Aktivierungszustand des Menüeintrags zugegriffen werden. Wird `false` an `setEnabled` übergeben, wird der Eintrag deaktiviert, andernfalls aktiviert. Ein deaktivierter Eintrag wird grau dargestellt und kann nicht ausgewählt werden.

Die beiden letzten Methoden `addActionListener` und `removeActionListener` dienen dazu, Objekte als Listener zu registrieren (bzw. zu deregistrieren). Solche Objekte müssen das Interface `ActionListener` implementieren. Sie werden beim Auswählen des Menüpunkts aktiviert, indem ihre Methode `actionPerformed` aufgerufen wird. Details können in Abschnitt 30.5 nachgelesen werden.

Das folgende Programm zeigt eine Swing-Anwendung mit einem einfachen "Datei"-Menü, das die Einträge "Öffnen", "Speichern" und "Beenden" besitzt. Das Menü und seine Einträge besitzen mnemonische Kürzel, und die Menüpunkte "Öffnen" und "Speichern" sind über die Beschleuniger `[STRG]`+`[O]` bzw. `[STRG]`+`[S]` zu erreichen. Das Applikationsobjekt registriert sich als `ActionListener` bei allen Menüpunkten und gibt die Benutzeraktionen auf der Console aus. Das Programm erzeugt zunächst eine neue Menüleiste und fügt ihr in Zeile 016 das in `createFileMenu` erzeugte Menü hinzu. Schließlich wird die Menüleiste mit `setJMenuBar` an das Hauptfenster übergeben.

    001 /* Listing3608.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005 import javax.swing.*;
    006
    007 public class Listing3608
    008 extends JFrame
    009 implements ActionListener
    010 {
    011   public Listing3608()
    012   {
    013     super("Swing-Menütest");
    014     addWindowListener(new WindowClosingAdapter(true));
    015     JMenuBar menubar = new JMenuBar();
    016     menubar.add(createFileMenu());
    017     setJMenuBar(menubar);
    018   }
    019
    020   public void actionPerformed(ActionEvent event)
    021   {
    022     System.out.println(event.getActionCommand());
    023   }
    024
    025   //---Private Methoden---------------
    026   private JMenu createFileMenu()
    027   {
    028     JMenu ret = new JMenu("Datei");
    029     ret.setMnemonic('D');
    030     JMenuItem mi;
    031     //Öffnen
    032     mi = new JMenuItem("Öffnen", 'f');
    033     setCtrlAccelerator(mi, 'O');
    034     mi.addActionListener(this);
    035     ret.add(mi);
    036     //Speichern
    037     mi = new JMenuItem("Speichern", 'p');
    038     setCtrlAccelerator(mi, 'S');
    039     mi.addActionListener(this);
    040     ret.add(mi);
    041     //Separator
    042     ret.addSeparator();
    043     //Beenden
    044     mi = new JMenuItem("Beenden", 'e');
    045     mi.addActionListener(this);
    046     ret.add(mi);
    047     return ret;
    048   }
    049
    050   private void setCtrlAccelerator(JMenuItem mi, char acc)
    051   {
    052     KeyStroke ks = KeyStroke.getKeyStroke(
    053       acc, Event.CTRL_MASK
    054     );
    055     mi.setAccelerator(ks);
    056   }
    057
    058   public static void main(String[] args)
    059   {
    060     Listing3608 frame = new Listing3608();
    061     frame.setLocation(100, 100);
    062     frame.setSize(300, 200);
    063     frame.setVisible(true);
    064   }
    065 }
| Listing3608.java

Listing 36.8: Ein Swing-Programm mit einem einfachen Menü

Das Programm sieht mit geöffnetem "Datei"-Menü so aus:

Abbildung 36.9: Ein Swing-Programm mit einem einfachen Menü

### 36.2.3 Weitere Möglichkeiten

#### Untermenüs

Das Einbinden von Untermenüs ist einfach. Da `Menu` aus `MenuItem` abgeleitet ist, kann an die Methode `add` der Klasse `Menu` auch eine Instanz der Klasse `Menu` übergeben werden. Der Name des Untermenüs erscheint dann an der Einfügestelle, und mit einem kleinen Pfeil wird angezeigt, daß es sich um ein Untermenü handelt.

#### Icons in Menüeinträgen

Einem Menüeintrag kann auch ein Icon zugeordnet werden. Dazu kann ein `Icon`-Objekt entweder direkt an den Konstruktor von `MenuItem` übergeben werden, oder es kann später durch Aufruf von `setIcon` zugewiesen werden. `Icon` ist ein Interface, das die abstrakten Eigenschaften eines Icons definiert. Es besitzt eine Implementierung `ImageIcon`, mit der sehr einfach aus einer gif- oder jpeg-Datei ein `Icon` erzeugt werden kann:

    public ImageIcon(String filename)
| javax.swing.ImageIcon

|  |  |  | Standardmäßig wird das Icon eines Menüeintrags links von seiner Beschriftung plaziert. Um es anders anzuordnen, kann auf dem Menüeintrag die Methode `setHorizontalTextPosition` aufgerufen und eine der Konstanten `RIGHT`, `LEFT`, `CENTER`, `LEADING` oder `TRAILING` aus dem Interface `SwingConstants` übergeben werden. |  |
Tip

|  | `SwingConstants` ist ein Interface im Paket `javax.swing`, das eine Reihe von Konstanten definiert, mit denen die Position und Orientierung von Swing-Komponenten beschrieben werden kann. Wir werden die Konstanten näher beschreiben, wenn sie als Parameter einer Methode auftauchen. Tabelle 36.3 listet alle Elemente überblicksartig auf:
Konstante | Bedeutung
`BOTTOM` | Unten
`CENTER` | An zentraler Position
`EAST` | Im Osten (rechts)
`HORIZONTAL` | Horizontal ausgerichtet
`LEADING` | Am Anfang eines anderen Elements
`LEFT` | Links
`NORTH` | Im Norden (oben)
`NORTH_EAST` | Im Nordosten (oben rechts)
`NORTH_WEST` | Im Nordwesten (oben links)
`RIGHT` | Rechts
`SOUTH` | Im Süden (unten)
`SOUTH_EAST` | Im Südosten (unten rechts)
`SOUTH_WEST` | Im Südwesten (unten links)
`TOP` | Oben
`TRAILING` | Am Ende eines anderen Elements
`VERTICAL` | Vertikal ausgerichtet
`WEST` | Im Westen (links)
Tabelle 36.3: Die Konstanten der Klasse SwingConstants |  |
Hinweis

#### Checkboxes und Radiobuttons in Menüeinträgen

Auch Checkboxen und Radiobuttons können in Menüeinträgen untergebracht werden. Das ist mit Hilfe der aus `JMenuItem` abgeleiteten Klassen `JCheckBoxMenuItem` und `JRadioButtonMenuItem` ebenfalls recht einfach.

`JCheckBoxMenuItem` stellt ähnliche Konstruktoren wie `JMenuItem` zur Verfügung und besitzt zusätzlich die Methoden `getState` und `setState`, mit denen auf seinen aktuellen Zustand zugegriffen werden kann. Wahlweise kann bereits an den Konstruktor der Anfangszustand der Checkbox übergeben werden:

    public JCheckBoxMenuItem(String text)
    public JCheckBoxMenuItem(String text, Icon icon)
    public JCheckBoxMenuItem(String text, boolean b)
    public JCheckBoxMenuItem(String text, Icon icon, boolean b)

    public boolean getState()
    public void setState(boolean b)
| javax.swing.JCheckBoxMenuItem

Im Gegensatz zu einem `JCheckBoxMenuItem` wird ein `JRadioButtonMenuItem` immer dann verwendet, wenn von mehreren Buttons nur einer zur Zeit aktiviert werden soll. Die wichtigsten Konstruktoren beider Klassen sind identisch:

    public JRadioButtonMenuItem(String text)
    public JRadioButtonMenuItem(String text, Icon icon)
    public JRadioButtonMenuItem(String text, boolean b)
    public JRadioButtonMenuItem(String text, Icon icon, boolean b)
| javax.swing.JRadioButtonMenuItem

Die Kontrolle des Zustands der Buttons erfolgt mit einem `ButtonGroup`-Objekt. Es wird vor dem Erzeugen der Menüeinträge angelegt, und jeder `JRadioButtonMenuItem` wird mit `add` hinzugefügt:

    public void add(AbstractButton b)
| javax.swing.ButtonGroup

Das `ButtonGroup`-Objekt sorgt automatisch dafür, daß zu jedem Zeitpunkt genau ein Eintrag selektiert ist. Auf den aktuellen Zustand jedes Menüeintrags kann mit Hilfe der Methoden `isSelected` und `setSelected` zugegriffen werden:

    public boolean isSelected()
    public void setSelected(boolean b)
| javax.swing.JRadioButtonMenuItem

Das folgende Programm zeigt alle weiterführenden Möglichkeiten im Überblick. Es definiert ein Menü "Extras", dessen oberster Eintrag "Tools" ein Untermenü mit sechs weiteren Einträgen ist. Darunter befinden sich zwei Checkbox- und drei RadioButton-Menüeinträge, die durch Anklicken aktiviert werden können. Der letzte Menüeintrag "Sicherheit" enthält zusätzlich ein Icon, das ein geöffnetes Vorhängeschloß zeigt.

    001 /* Listing3609.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005 import javax.swing.*;
    006
    007 public class Listing3609
    008 extends JFrame
    009 {
    010   public Listing3609()
    011   {
    012     super("Swing-Menütest II");
    013     addWindowListener(new WindowClosingAdapter(true));
    014     JMenuBar menubar = new JMenuBar();
    015     menubar.add(createExtrasMenu());
    016     setJMenuBar(menubar);
    017   }
    018
    019   //---Private Methoden---------------
    020   private JMenu createExtrasMenu()
    021   {
    022     JMenu ret = new JMenu("Extras");
    023     ret.setMnemonic('X');
    024     JMenuItem mi;
    025     //Tools-Untermenü
    026     ret.add(createToolsSubMenu());
    027     //Separator
    028     ret.addSeparator();
    029     //Statuszeile und Buttonleiste
    030     mi = new JCheckBoxMenuItem("Statuszeile");
    031     mi.setMnemonic('z');
    032     ((JCheckBoxMenuItem)mi).setState(true);
    033     ret.add(mi);
    034     mi = new JCheckBoxMenuItem("Buttonleiste");
    035     mi.setMnemonic('B');
    036     ret.add(mi);
    037     //Separator
    038     ret.addSeparator();
    039     //Offline, Verbinden, Anmelden
    040     ButtonGroup bg = new ButtonGroup();
    041     mi = new JRadioButtonMenuItem("Offline", true);
    042     mi.setMnemonic('O');
    043     ret.add(mi);
    044     bg.add(mi);
    045     mi = new JRadioButtonMenuItem("Verbinden");
    046     mi.setMnemonic('V');
    047     ret.add(mi);
    048     bg.add(mi);
    049     mi = new JRadioButtonMenuItem("Anmelden");
    050     mi.setMnemonic('A');
    051     ret.add(mi);
    052     bg.add(mi);
    053     //Separator
    054     ret.addSeparator();
    055     //Sicherheit
    056     mi = new JMenuItem(
    057       "Sicherheit",
    058       new ImageIcon("lock.gif")
    059     );
    060     mi.setMnemonic('S');
    061     mi.setHorizontalTextPosition(JMenuItem.LEFT);
    062     ret.add(mi);
    063     return ret;
    064   }
    065
    066   private JMenu createToolsSubMenu()
    067   {
    068     JMenu ret = new JMenu("Tools");
    069     ret.setMnemonic('T');
    070     ret.add(new JMenuItem("Rechner", 'R'));
    071     ret.add(new JMenuItem("Editor",  'E'));
    072     ret.add(new JMenuItem("Browser", 'B'));
    073     ret.add(new JMenuItem("Zipper",  'Z'));
    074     ret.add(new JMenuItem("Snapper", 'S'));
    075     ret.add(new JMenuItem("Viewer",  'V'));
    076     return ret;
    077   }
    078
    079   public static void main(String[] args)
    080   {
    081     Listing3609 frame = new Listing3609();
    082     frame.setLocation(100, 100);
    083     frame.setSize(300, 200);
    084     frame.setVisible(true);
    085   }
    086 }
| Listing3609.java

Listing 36.9: Weitere Möglichkeiten von Swing-Menüs

Mit geöffneten Menüs stellt sich das Programm so dar:

Abbildung 36.10: Ein Swing-Programm mit einem umfangreichen Menü

|  |  |  | Im JDK 1.3 arbeitet das Programm unter Windows fehlerhaft. Nach einmaligem Anklicken des Menüeintrags »Sicherheit« kann das Untermenü »Tools« nicht mehr per Maus aufgerufen werden, weil im Event-Thread eine `NullPointerException` ausgelöst wird. Die Tastaturbedienung ist dagegen weiterhin möglich. Dieser seit dem RC2 des JDK 1.3 bekannte Fehler wurde leider bis zur endgültigen Version des JDK 1.3 nicht mehr behoben. Seit dem JDK 1.3.1 tritt er allerdings nicht mehr auf. |  |
JDK1.1-1.4

### 36.2.4 Kontextmenüs

Kontextmenüs für AWT-Programme wurden bereits in Abschnitt 30.6 ausführlich besprochen. In Swing-Programmen werden Kontextmenüs mit Hilfe der Klasse `JPopupMenu` erzeugt. Sie kann parameterlos instanziert werden und stellt Methoden ähnlich `JMenu` zur Verfügung:

    public JMenuItem add(JMenuItem menuItem)
    public JMenuItem add(String s)

    public void addSeparator()

    public void show(Component invoker, int x, int y)
| javax.swing.JPopupMenu

Mit `add` werden Menüeinträge hinzugefügt, mit `addSeparator` eine Trennlinie. Kontextmenüs können prinzipiell ebenso komplex aufgebaut sein wie normale Menüs, also inbesondere Icons, Untermenüs, Checkboxen oder Radiobuttons enthalten. Um das Menü anzuzeigen, ist `show` aufzurufen. Die dazu erforderlichen Koordinaten gewinnt man am besten aus der aktuellen Position des Mauszeigers, die im Mausereignis mitgeliefert wird.

Die zum Aktivieren eines Kontextmenüs erforderliche Mausaktion kann von Plattform zu Plattform unterschiedlich sein. Die portabelste Lösung besteht darin, einen `MouseListener` auf der Komponente zu registrieren, und bei jedem Mausereignis mit `isPopupTrigger` abzufragen, ob es sich um eine Aktion zum Aufrufen eines Kontextmenüs handelte.

Das folgende Programm zeigt ein Hauptfenster, auf dem ein Kontextmenü mit den Einträgen "Rueckgaengig", "Ausschneiden", "Kopieren" und "Einfuegen" aufgerufen werden kann. Der Aufruf des Kontextmenüs erfolgt durch Abfrage der Mausereignisse. Beim Auswählen einer Menüaktion wird der Kommandoname auf der Konsole ausgegeben.

    001 /* Listing3610.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005 import javax.swing.*;
    006 import javax.swing.border.*;
    007
    008 public class Listing3610
    009 extends JFrame
    010 implements MouseListener, ActionListener
    011 {
    012   public Listing3610()
    013   {
    014     super("Kontextmenüs");
    015     addWindowListener(new WindowClosingAdapter(true));
    016     addMouseListener(this);
    017   }
    018
    019   //MouseListener
    020   public void mouseClicked(MouseEvent event)
    021   {
    022     checkPopupMenu(event);
    023   }
    024
    025   public void mouseEntered(MouseEvent event)
    026   {
    027   }
    028
    029   public void mouseExited(MouseEvent event)
    030   {
    031   }
    032
    033   public void mousePressed(MouseEvent event)
    034   {
    035     checkPopupMenu(event);
    036   }
    037
    038   public void mouseReleased(MouseEvent event)
    039   {
    040     checkPopupMenu(event);
    041   }
    042
    043   private void checkPopupMenu(MouseEvent event)
    044   {
    045     if (event.isPopupTrigger()) {
    046       JPopupMenu popup = new JPopupMenu();
    047       //Rückgängig hinzufügen
    048       JMenuItem mi = new JMenuItem("Rueckgaengig");
    049       mi.addActionListener(this);
    050       popup.add(mi);
    051       //Separator hinzufügen
    052       popup.addSeparator();
    053       //Ausschneiden, Kopieren, Einfügen hinzufügen
    054       mi = new JMenuItem("Ausschneiden");
    055       mi.addActionListener(this);
    056       popup.add(mi);
    057       mi = new JMenuItem("Kopieren");
    058       mi.addActionListener(this);
    059       popup.add(mi);
    060       mi = new JMenuItem("Einfuegen");
    061       mi.addActionListener(this);
    062       popup.add(mi);
    063       //Menü anzeigen
    064       popup.show(
    065         event.getComponent(),
    066         event.getX(),
    067         event.getY()
    068       );
    069     }
    070   }
    071
    072   //ActionListener
    073   public void actionPerformed(ActionEvent event)
    074   {
    075     System.out.println(event.getActionCommand());
    076   }
    077
    078   public static void main(String[] args)
    079   {
    080     Listing3610 frame = new Listing3610();
    081     frame.setLocation(100, 100);
    082     frame.setSize(300, 200);
    083     frame.setVisible(true);
    084   }
    085 }
| Listing3610.java

Listing 36.10: Anzeigen eines Kontextmenüs in Swing

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de