Titel | Inhalt | Suchen | Index | API | Go To Java 2, Zweite Auflage, Handbuch der Java-Programmierung |
<< | < | > | >> | Kapitel 36 - Swing: Container und Menüs |
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. |
|
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. |
|
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()); 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 |
Das Programm sieht mit geöffnetem "Datei"-Menü so aus:
Abbildung 36.9: Ein Swing-Programm mit einem einfachen Menü
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.
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. |
|
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:
Tabelle 36.3: Die Konstanten der Klasse SwingConstants |
|
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()); 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 |
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 bekannte Fehler wurde leider bis zur endgültigen Version des JDK 1.3 nicht mehr behoben. |
|
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()); 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 |
Titel | Inhalt | Suchen | Index | API | Go To Java 2, Zweite Auflage, Addison Wesley, Version 2.0 |
<< | < | > | >> | © 2000 Guido Krüger, http://www.gkrueger.com |