|
|
|
|
|
|
|
|
|
|
|
|
|
| Et vindue har ofte en menubar med menuer, der giver mulighed for at
vælge forskellige handlinger man ønsker udført, i
forbindelse med den applikation som vinduet tilhører. Vi vil derfor
studere mulighederne for at lave og anvende sådanne menuer i Java.
|
|
| Det er en større proces at implemnetere en menubar, og vi vil
derfor arbejde os frem mod målet i flere mindre skridt.
|
|
| Følgende vindue:
|
Figur 1:
Vindue med tom menubar
|
|
|
| Laves af programmet:
|
Source 1:
Tilføjelse af JMenuBar
|
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MenuFrame extends JFrame {
public MenuFrame( String titel ) {
super( titel );
setJMenuBar( makeMenuBar() );
addWindowListener( new WindowHandler() );
setSize( 300, 100 );
setVisible( true );
}
/*
* Lave menubar med menuer
*/
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
//...
return bar;
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
| Vi vælger selv at sætte størrelsen af vinduet, da
vi i første omgang ikke er interesseret i at tilføje det
komponenter.
|
|
| Hvis man ser godt efter i figur 1, kan man svagt ane, at der er et smalt
lysegråt område (nærmest en linie) under vinduets hoved.
Denne linie er en tom menubar. Vi har endnu ikke føjet menuer til
menubaren og den er derfor "klappet sammen".
|
|
| Man ser at vi har valgt at placere opbygningen af menubaren i en intern
service-metode. Det gør man normalt, da opbygningen af en menu
kan fylde endog mange linier, og det derfor er en fordel at få disse
linier ud af selve kontruktoren. Den eneste linie i konstruktoren som
vedrører menubaren, vil derfor være linie med kaldet af setJMenuBar,
der føjer menubaren til framen. I det følgende vil vi derfor
udelukkende beskæftige os med indholdet af metoden makeMenuBar.
|
|
| En menubar er i Java en instans af JMenuBar.
JMenuBar er en container,
som kan indeholde menuer. Disse menuer placeres hen ad menubaren i den
rækkefølge de er blevet tilføjet, med den sædvanlie
container-metode add.
|
|
| Hver enkelt menu er en instans af JMenu, der ligeledes er en container.
En JMenu har en konstruktor, der tager en String som parameter. Denne
parameter er navnet på menuen.
|
|
| Hvis vi udbyggede makeMenuBar
med:
|
Source 2:
JMenuBar med
to menuer
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu fileMenu = new JMenu( "File" );
JMenu helpMenu = new JMenu( "Help" );
bar.add( fileMenu );
bar.add( helpMenu );
return bar;
}
|
|
|
| Vores vindue får nu følgende udseende med de to menuer
i menubaren:
|
Figur 2:
Vindue med to menuer
|
|
|
| Nederst har vi aktiveret den første menu, hvorved den bliver
blå, men da den endnu ikke indeholder nogen menu-punkter er der
kun en lille "tap", hvor indholdet skulle have været.
|
|
| Som nævnt er en JMenu
en container. En JMenu
kan indeholde instanser af JMenuItem
eller JMenu (se evt. Composite
Pattern for den generelle design idé). I det sidste tilfælde
vil der være tale om undermenuer.
|
|
| Lad os føje nogle JMenuItem's
til den første af menuerne:
|
Source 3:
JMenuItem's
i menu
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu fileMenu = new JMenu( "File" );
JMenuItem newMI = new JMenuItem( "New" );
fileMenu.add( newMI );
JMenuItem openMI = new JMenuItem( "Open" );
fileMenu.add( openMI );
JMenuItem closeMI = new JMenuItem( "Close" );
fileMenu.add( closeMI );
fileMenu.addSeparator();
JMenuItem saveMI = new JMenuItem( "Save" );
fileMenu.add( saveMI );
JMenu helpMenu = new JMenu( "Help" );
bar.add( fileMenu );
bar.add( helpMenu );
return bar;
}
|
|
|
| Det er nyttigt at anvende indrykninger for at fremhæve sammenhængen
mellem menuer og deres indhold.
|
|
| Menuen før følgende udseende:
|
Figur 3:
Menu med menu-punkter
|
|
|
| Den kosmetiske streng der adskiller de tre øverste menu-punkter
fra det nederste, laves ved at kalde metoden addSeparator
på det ønskede sted i sekvensen af add-kald.
|
|
|
1. Event-håndtering
|
|
| Når brugeren vælger et menu-punkt udløses der en
ActionEvent. Når
vi ønsker at modtage events vedrørende valg af menu-punkter,
skal vi blot tilmelde os som ActionListener
hos alle menu-punkter og gemme en reference til dem, for senere identifikation
af kilden til event'en.
|
|
| For at illustrere teknikken kun vi lade programmet skrive hvilket menu-punkt,
der er tale om, når det bliver valgt:
|
Source 4:
Events fra menu-punkter
|
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MenuFrame extends JFrame implements ActionListener {
private JMenuItem newMI, openMI, closeMI, saveMI;
public MenuFrame( String titel ) {
super( titel );
setJMenuBar( makeMenuBar() );
addWindowListener( new WindowHandler() );
setSize( 300, 100 );
setVisible( true );
}
/*
* Lave menubar med menuer
*/
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu fileMenu = new JMenu( "File" );
newMI = new JMenuItem( "New" );
newMI.addActionListener( this );
fileMenu.add( newMI );
openMI = new JMenuItem( "Open" );
openMI.addActionListener( this );
fileMenu.add( openMI );
closeMI = new JMenuItem( "Close" );
closeMI.addActionListener( this );
fileMenu.add( closeMI );
fileMenu.addSeparator();
saveMI = new JMenuItem( "Save" );
saveMI.addActionListener( this );
fileMenu.add( saveMI );
JMenu helpMenu = new JMenu( "Help" );
bar.add( fileMenu );
bar.add( helpMenu );
return bar;
}
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
if ( source == newMI )
System.out.println( "new" );
else if ( source == openMI )
System.out.println( "open" );
else if ( source == closeMI )
System.out.println( "close" );
else if ( source == saveMI )
System.out.println( "save" );
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
|
|
| Prøv:
|
Kør eksemplet - vælg nogle af menu-punkterne!
|
|
|
|
|
|
|
|
|
Instanser af JMenuItem
er ikke de eneste menu-punkter man kan føje til en menu. Der
findes to andre klasser: JCheckBoxMenuItem
og JRadioButtonMenuItem,
der subklasser af JMenuItem,
som også kan tilføjes en JMenu
JMenu er selv en subklasse af JMenuItem,
i det der fremstår som et Composite
Pattern:
|
Figur 4:
JMenuItem og dens subklasser
|
|
|
|
2. Undermenuer
|
|
| Det er enkelt at lave undermenuer. En undermenu er ganske enkelt en
JMenu, der er add'ed
til en JMenu som ethvert
andet JMenuItem.
|
|
| F.eks.:
|
Figur 5:
Undermenu
|
|
|
| Der laves med følgende implementation af makeMenuBar:
|
Source 5:
Menu med en undermenu
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu hovedMenu = new JMenu( "Hoved menu titel" );
hovedMenu.add( new JMenuItem( "Hoved menu item 1" ) );
JMenu underMenu = new JMenu( "Under menu titel" );
underMenu.add( new JMenuItem( "Under menu item 1" ) );
underMenu.add( new JMenuItem( "Under menu item 2" ) );
underMenu.add( new JMenuItem( "Under menu item 3" ) );
hovedMenu.add( underMenu );
hovedMenu.add( new JMenuItem( "Hoved menu item 2" ) );
bar.add( hovedMenu );
return bar;
}
|
|
|
|
3. JCheckBoxMenuItem
|
|
| Menu-punkter, som er instanser af JCheckBoxMenuItem,
har et tag som enten kan være sat eller ej. Det betyder at menu-punktet
fungerer som en kontakt, der enten kan være slået til eller
fra.
|
|
| Betragt følgende menu:
|
Figur 6:
Menu med JCheckBox-MenuItems
|
|
|
| Her optræder der en firkant til venstre for menu-punktet. Denne
firkant giver mulighed for at et menu-punkt kan være valgt eller
ej. Hvis vi vælger de to menu-punkter: Skrå skrift
og Fed skrift, bliver billedet:
|
Figur 7:
To valgte JCheckBox-MenuItems
|
|
|
| Default er JCheckBoxMenuItems
ikke valgt, men ønsker man at et sådant menu-punkt fra starten
skal være valgt kan man bruge metoden setState,
der har en tilsvarende get-metode:
|
|
|
boolean getState()
void setState( boolean b )
|
|
|
| Lad os for eksemplets skyld se en implementation af makeMenuBar,
der laver ovenstående vindue og initielt sætter de to af menu-punkterne,
svarende til ovenstående figur:
|
Source 6:
Menu med tre JCheckBox-MenuItems
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu typoMenu = new JMenu( "Typografi" );
skråMI = new JCheckBoxMenuItem( "Skrå skrift" );
skråMI.setState( true );
typoMenu.add( skråMI );
underMI = new JCheckBoxMenuItem( "Underlinieret" );
underMI.setState( false );
typoMenu.add( underMI );
fedMI = new JCheckBoxMenuItem( "Fed skrift" );
fedMI.setState( true );
typoMenu.add( fedMI );
bar.add( typoMenu );
return bar;
}
|
|
| Item-Listener
| I forbindelse med event-håndtering kan man vælge enten at
tilmelde sig som ItemListener
eller som ActionListener.
Det er ligegyldigt hvilken man vælger. Det skyldes at en ændring
af tilstanden (valgt/ikke valgt) altid hænger sammen med om menu-punktet
aktiveres. Hvis der andre dele af systemet der kan ændre menu-punktets
tilstand skal man tilmelde sig som ItemListener, ellers er det uden betydning.
|
|
| Vi vil for eksemplets skyld vælge at være ItemListener,
da vi allerede har optrådt som ActionListener
i dette kapitel.
|
|
| Som ItemListener skal
vi implementere metoden:
|
|
|
void itemStateChanged( ItemEvent e )
|
|
|
| Hvis vi indskrænker os til kun at meddele ændringens karakter,
får det samlede program følgende udseende:
|
Source 7:
Håndtering af events fra de tre menu-punkter
|
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MenuFrame extends JFrame implements ItemListener {
private JCheckBoxMenuItem skråMI, underMI, fedMI;
public MenuFrame( String titel ) {
super( titel );
setJMenuBar( makeMenuBar() );
addWindowListener( new WindowHandler() );
setSize( 300, 100 );
setVisible( true );
}
/*
* Lave menubar med menuer
*/
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu typoMenu = new JMenu( "Typografi" );
skråMI = new JCheckBoxMenuItem( "Skrå skrift" );
skråMI.setState( true );
skråMI.addItemListener( this );
typoMenu.add( skråMI );
underMI = new JCheckBoxMenuItem( "Underlinieret" );
underMI.setState( false );
underMI.addItemListener( this );
typoMenu.add( underMI );
fedMI = new JCheckBoxMenuItem( "Fed skrift" );
fedMI.setState( true );
fedMI.addItemListener( this );
typoMenu.add( fedMI );
bar.add( typoMenu );
return bar;
}
public void itemStateChanged( ItemEvent e ) {
Object source = e.getSource();
if ( source == skråMI )
System.out.println( "skrå: " + skråMI.getState() );
else if ( source == underMI )
System.out.println( "under: " + underMI.getState() );
else if ( source == fedMI )
System.out.println( "fed: " + fedMI.getState() );
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
|
|
| Prøv:
|
Kør eksemplet - slå nogle af menu-punkterne
fra og til!
|
|
|
4. JRadioButtonMenuItem
|
|
| Instanser af JRadioButtonMenuItem
ligner meget instanser af JRadioButton.
Vi vil derfor ikke beskrive egenskaberne ved JRadioButtons,
men kun beskrive implementationen, idet der henvises til kapitlet om JRadioButton.
|
|
| JRadioButtonMenuItem
har de samme konstruktorer som JRadioButton
og de har samme funktionalitet.
|
|
| Som for JCheckBoxMenuItem
kan vi vælge mellem at være ActionListener
eller ItemListener. Vi
vælger igen at være ItemListener,
da vi ellers vil få event hvis brugeren vælger et menu-punkt
der allerede er valgt. Det er tilstandsændringer vi er interesseret
i, og som ItemListener
er det kun dem vi hører om.
|
|
| Lad os se et eksempel. Betragt følgende vindue:
|
Figur 8:
Tre radiobuttons
i vindue
|
|
|
| Dette vindue laves med følgende frame
|
Source 8:
Frame med tre JRadioButton-MenuItems
|
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MenuFrame extends JFrame implements ItemListener {
private JRadioButtonMenuItem p8MI, p10MI, p12MI;
public MenuFrame( String titel ) {
super( titel );
setJMenuBar( makeMenuBar() );
addWindowListener( new WindowHandler() );
setSize( 300, 100 );
setVisible( true );
}
/*
* Lave menubar med menuer
*/
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu typoMenu = new JMenu( "Font størrelse" );
ButtonGroup gruppen = new ButtonGroup();
p8MI = new JRadioButtonMenuItem( "8", true );
gruppen.add( p8MI );
p8MI.addItemListener( this );
typoMenu.add( p8MI );
p10MI = new JRadioButtonMenuItem( "10" );
gruppen.add( p10MI );
p10MI.addItemListener( this );
typoMenu.add( p10MI );
p12MI = new JRadioButtonMenuItem( "12" );
gruppen.add( p12MI );
p12MI.addItemListener( this );
typoMenu.add( p12MI );
bar.add( typoMenu );
return bar;
}
public void itemStateChanged( ItemEvent e ) {
Object source = e.getSource();
if ( source == p8MI )
System.out.println( "8: " + p8MI.isSelected() );
else if ( source == p10MI )
System.out.println( "10: " + p10MI.isSelected() );
else if ( source == p12MI )
System.out.println( "12: " + p12MI.isSelected() );
}
class WindowHandler extends WindowAdapter {
public void windowClosing( WindowEvent e ) {
dispose();
System.exit(0);
}
}
}
|
|
|
| Som det ses bruger vi en ButtonGroup
til at opnå indbyrdes udelukkelse af de tre radiobuttons; hvilket
er det almidelige for JRadioButtons.
|
|
|
5. Shortcuts
|
|
| Det er muligt at lave to former for shortcuts til menu-punkter: mnemonics
og accelerators. Det skal bemærkes at der alle steder i det
følgende anvendes store bogstaver for taster. Shortkeys er ikke
case-sensitive, men det er gjort for at det skulle blive ensartet.
|
|
|
5.1 Mnemonics
|
| Alt-tasten
| Alle mnemonics består af Alt-tasten og et bogstav. Det er derfor
kun bogstavet man anfører når man laver et mnemonic.
|
|
| Hvis vi vender tilbage til eksemplet med en file-menu, vil den med de
normale shortcuts få følgende udseende:
|
Figur 9:
Mnemonics i menu
|
|
|
| Menuen laves med følgende implementation af makeMenuBar:
|
Source 9:
Angivelse af mnemonics
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu fileMenu = new JMenu( "File" );
fileMenu.setMnemonic( 'F' );
newMI = new JMenuItem( "New" );
newMI.setMnemonic( 'N' );
newMI.addActionListener( this );
fileMenu.add( newMI );
openMI = new JMenuItem( "Open" );
openMI.setMnemonic( 'O' );
openMI.addActionListener( this );
fileMenu.add( openMI );
closeMI = new JMenuItem( "Close" );
closeMI.setMnemonic( 'C' );
closeMI.addActionListener( this );
fileMenu.add( closeMI );
fileMenu.addSeparator();
saveMI = new JMenuItem( "Save" );
saveMI.setMnemonic( 'S' );
saveMI.addActionListener( this );
fileMenu.add( saveMI );
JMenu helpMenu = new JMenu( "Help" );
bar.add( fileMenu );
bar.add( helpMenu );
return bar;
}
|
|
|
| Man bemærker at menu-punkterne får underlinieret det tegn,
som svarer til mnemonic. Hvis man vælger et mnemonic der ikke findes
i menu-punktets tekst, kommer der ingen underliniering, men mnemonic virker
alligevel.
|
|
|
5.2 Accelerators
|
|
| Acceleratorer kræver ikke at de gøres "synlige",
ved at man klikker sig frem til dem via en række mnemonics. Selvom
ingen menu er åbnet kan man blot anvende taste-kombinationen, og
menu-punktet bliver aktiveret. Man kan af samme grund ikke knytte en accelerator
til en menu, men kun til egentlige menu-punkter.
|
|
| Hvis vi igen anvender eksemplet med file-menuen, kunne vi for eksemplets
skyld lave følgende shortcuts, vha. accelerators:
|
Figur 10:
Accelerators i menu
|
|
| Alt, Ctrl eller Shift
| Som man ser kan disse shortcuts laves med enkelte taster, f.eks. 'N',
eller en eller flere af tasterne Alt, Ctrl eller Shift,
som kan kombineres frit. Ved at anvende Shift gør man shortcut'et
case-sensitivt.
|
|
| Menuen laves med følgende implementation af makeMenuBar:
|
Source 10:
Angivelse af accelerators
|
private JMenuBar makeMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu fileMenu = new JMenu( "File" );
fileMenu.setMnemonic( 'F' );
newMI = new JMenuItem( "New" );
newMI.setAccelerator( KeyStroke.getKeyStroke( 'N', 0 ) );
newMI.addActionListener( this );
fileMenu.add( newMI );
openMI = new JMenuItem( "Open" );
openMI.setAccelerator( KeyStroke.getKeyStroke( 'O', Event.CTRL_MASK ) );
openMI.addActionListener( this );
fileMenu.add( openMI );
closeMI = new JMenuItem( "Close" );
closeMI.setAccelerator(
KeyStroke.getKeyStroke( 'C', Event.SHIFT_MASK + Event.ALT_MASK ) );
closeMI.addActionListener( this );
fileMenu.add( closeMI );
fileMenu.addSeparator();
saveMI = new JMenuItem( "Save" );
saveMI.setAccelerator(
KeyStroke.getKeyStroke( 'S', Event.CTRL_MASK + Event.ALT_MASK ) );
saveMI.addActionListener( this );
fileMenu.add( saveMI );
JMenu helpMenu = new JMenu( "Help" );
bar.add( fileMenu );
bar.add( helpMenu );
return bar;
}
|
|
|
| Til at sætte en accelerator for et menu-punkt kaldes metoden:
|
|
|
void setAccelerator( KeyStroke keyStroke )
|
|
|
| Til at lave instansen af KeyStroke
bruges følgende metode:
|
|
|
KeyStroke KeyStroke.getKeyStroke( int keyCode, int modifiers )
|
|
|
| hvor keyCode er UniCode
for det pågældende tegn og modifiers er supplerende egenskaber
for "taste-trykket". Disse modifiers kan være en eller
flere af følgende konstanter:
|
|
|
Event.SHIFT_MASK
Event.CTRL_MASK
Event.META_MASK
Event.ALT_MASK
|
|
|
| Konstanterne er defineret som 1, 2, 4, 8 og de kan derfor kombineres
ved simpel addition, som det er gjort i eksemplet ovenfor. Mig bekendte
findes Meta-tasten ikke under Win32 (den findes under UNIX).
|
|
|
|
|
| |