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.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.1
 <<    <     >    >>   API  © 1998-2003 Guido Krüger, http://www.javabuch.de