如何使用菜单

菜单提供了一种节省空间的方式,使用户可以选择多个选项之一。用户可以选择的其他组件包括combo boxeslistsradio buttonsspinnerstool bars。如果您的任何菜单项执行的操作都与另一个菜单项或工具栏按钮重复,那么除了本节之外,您还应该阅读如何使用动作

菜单的独特之处在于,按照惯例,它们不会与 UI 中的其他组件一起放置。取而代之的是,菜单通常显示在菜单栏弹出菜单中。菜单栏包含一个或多个菜单,并具有一个习惯于平台的位置-通常在窗口顶部。弹出菜单是不可见的菜单,直到用户在启用了弹出菜单的组件上进行特定于平台的鼠标操作(例如,按下鼠标右键)为止。然后,弹出菜单出现在光标下方。

下图显示了许多与菜单相关的组件:菜单栏,菜单,菜单项,单选按钮菜单项,复选框菜单项和分隔符。如您所见,菜单项可以具有图像或文本,或两者都有。您还可以指定其他属性,例如字体和颜色。

MenuLookDemo

本节的其余部分将教您有关菜单组件的知识,并告诉您如何使用各种菜单功能:

菜单组件层次结构

这是菜单相关类的继承层次结构的图片:

菜单类的继承层次结构

如图所示,菜单项(包括菜单)只是buttons。您可能想知道菜单(如果只是一个按钮)如何显示其菜单项。答案是,激活菜单后,它会自动弹出一个显示菜单项的弹出菜单。

Creating Menus

以下代码创建了在此菜单部分开头附近显示的菜单。粗体代码创建并连接菜单对象。其他代码设置或自定义菜单对象。您可以在MenuLookDemo.java中找到整个程序。其他必需的文件在example index中列出。

Try this:

因为此代码没有事件处理,所以菜单除了看上去应有的外没有任何用处。如果运行该示例,则会注意到,尽管缺少自定义事件处理功能,但菜单和子菜单在应有的时候出现,并且复选框和单选按钮在用户选择时会适当响应。

//Where the GUI is created:
JMenuBar menuBar;
JMenu menu, submenu;
JMenuItem menuItem;
JRadioButtonMenuItem rbMenuItem;
JCheckBoxMenuItem cbMenuItem;

//Create the menu bar.
menuBar = new JMenuBar();

//Build the first menu.
menu = new JMenu("A Menu");
menu.setMnemonic(KeyEvent.VK_A);
menu.getAccessibleContext().setAccessibleDescription(
        "The only menu in this program that has menu items");
menuBar.add(menu);

//a group of JMenuItems
menuItem = new JMenuItem("A text-only menu item",
                         KeyEvent.VK_T);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_1, ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
        "This doesn't really do anything");
menu.add(menuItem);

menuItem = new JMenuItem("Both text and icon",
                         new ImageIcon("images/middle.gif"));
menuItem.setMnemonic(KeyEvent.VK_B);
menu.add(menuItem);

menuItem = new JMenuItem(new ImageIcon("images/middle.gif"));
menuItem.setMnemonic(KeyEvent.VK_D);
menu.add(menuItem);

//a group of radio button menu items
menu.addSeparator();
ButtonGroup group = new ButtonGroup();
rbMenuItem = new JRadioButtonMenuItem("A radio button menu item");
rbMenuItem.setSelected(true);
rbMenuItem.setMnemonic(KeyEvent.VK_R);
group.add(rbMenuItem);
menu.add(rbMenuItem);

rbMenuItem = new JRadioButtonMenuItem("Another one");
rbMenuItem.setMnemonic(KeyEvent.VK_O);
group.add(rbMenuItem);
menu.add(rbMenuItem);

//a group of check box menu items
menu.addSeparator();
cbMenuItem = new JCheckBoxMenuItem("A check box menu item");
cbMenuItem.setMnemonic(KeyEvent.VK_C);
menu.add(cbMenuItem);

cbMenuItem = new JCheckBoxMenuItem("Another one");
cbMenuItem.setMnemonic(KeyEvent.VK_H);
menu.add(cbMenuItem);

//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);

menuItem = new JMenuItem("An item in the submenu");
menuItem.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_2, ActionEvent.ALT_MASK));
submenu.add(menuItem);

menuItem = new JMenuItem("Another item");
submenu.add(menuItem);
menu.add(submenu);

//Build second menu in the menu bar.
menu = new JMenu("Another Menu");
menu.setMnemonic(KeyEvent.VK_N);
menu.getAccessibleContext().setAccessibleDescription(
        "This menu does nothing");
menuBar.add(menu);

...
frame.setJMenuBar(theJMenuBar);

如代码所示,要设置JFrame的菜单栏,请使用setJMenuBar方法。要将JMenu添加到JMenuBar,请使用add(JMenu)方法。要将菜单项和子菜单添加到JMenu,请使用add(JMenuItem)方法。

Note:

菜单项与其他组件一样,最多可以位于一个容器中。如果try将菜单项添加到第二个菜单,则该菜单项将从第一个菜单中删除,然后再添加到第二个菜单。有关实现执行相同功能的多个组件的方法,请参见如何使用动作

前面代码中的其他方法包括setAcceleratorsetMnemonic,稍后将在启用键盘操作中进行讨论。 如何支持辅助技术中讨论了setAccessibleDescription方法。

处理菜单项中的事件

要检测用户何时选择JMenuItem,您可以监听动作事件(就像JButton一样)。要检测用户何时选择JRadioButtonMenuItem,您可以监听动作事件或项目事件,如如何使用单选按钮中所述。对于JCheckBoxMenuItem,通常按照如何使用复选框所述监听项目事件。

Try this:

文本区域显示了侦听器检测到的操作和项目事件。

以下是实现事件处理的代码:

public class MenuDemo ... implements ActionListener,
                                     ItemListener {
    ...
    public MenuDemo() {
        //...for each JMenuItem instance:
        menuItem.addActionListener(this);
        ...
        //for each JRadioButtonMenuItem: 
        rbMenuItem.addActionListener(this);
        ...
        //for each JCheckBoxMenuItem: 
        cbMenuItem.addItemListener(this);
        ...
    }

    public void actionPerformed(ActionEvent e) {
        //...Get information from the action event...
        //...Display it in the text area...
    }

    public void itemStateChanged(ItemEvent e) {
        //...Get information from the item event...
        //...Display it in the text area...
    }

有关处理动作和项目事件的示例,请参见buttonradio buttoncheck box部分,以及本节末尾的范例清单

启用键盘操作

菜单支持两种键盘选择:助记符和加速器。 助记符提供了一种使用键盘导航菜单层次结构的方法,从而增加了程序的可访问性。另一方面,“加速器” *提供键盘快捷键来“绕过”导航菜单层次结构。助记符适用于所有用户;加速器适用于高级用户。

助记键是用于选择已经可见的菜单项的键。例如,在MenuDemo中,第一个菜单具有助记符 A,第二个菜单项具有助记符 B。这意味着,当您以 Java 外观运行MenuDemo时,按 Alt 和 A 键将显示第一个菜单。当第一个菜单可见时,按 B 键(带有或不带有 Alt)可以选择第二个菜单项。菜单项通常通过在菜单项的文本中首次出现助记符来强调其助记符,如以下快照所示。

B 是此菜单项的助记符

加速器是一种组合键,无论菜单项是否可见,它都会导致菜单项被选择。例如,按MenuDemo中的 Alt 和 2 键可以选择第一个菜单的子菜单中的第一项,而不会弹出任何菜单。只有叶子菜单项(不显示其他菜单的菜单)可以具有加速器。以下快照显示了 Java 的外观如何显示具有加速器的菜单项。

Alt 2 广告此菜单项的加速器

您可以在构造菜单项时或使用setMnemonic方法来指定助记符。若要指定加速器,请使用setAccelerator方法。以下是设置助记符和加速器的示例:

//Setting the mnemonic when constructing a menu item:
menuItem = new JMenuItem("A text-only menu item",
                         KeyEvent.VK_T);

//Setting the mnemonic after creation time:
menuItem.setMnemonic(KeyEvent.VK_T);

//Setting the accelerator:
menuItem.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_T, ActionEvent.ALT_MASK));

如您所见,您可以通过指定与用户应按的键相对应的KeyEvent常量来设置助记符。要指定加速器,您必须使用KeyStroke对象,该对象将键(由KeyEvent常量指定)和修饰键掩码(由ActionEvent常量指定)组合在一起。

Note:

由于弹出菜单与常规菜单不同,并不总是包含在组件中,因此除非可见弹出菜单,否则弹出菜单项中的加速器将不起作用。

弹出菜单

要弹出一个弹出菜单(JPopupMenu),必须在与该弹出菜单相关联的每个组件上注册一个鼠标侦听器。鼠标侦听器必须检测到用户要求弹出菜单的请求。

应显示弹出菜单的确切手势因外观而异。在 Microsoft Windows 中,按照惯例,用户将光标悬停在启用了弹出框的组件上,并通过释放鼠标右键来弹出弹出菜单。在 Java 外观中,惯用触发器是按下鼠标右键(对于释放按钮时弹出的弹出窗口)或单击它(对于弹出的弹出窗口)。

Try this:

//...where instance variables are declared:
JPopupMenu popup;

    //...where the GUI is constructed:
    //Create the popup menu.
    popup = new JPopupMenu();
    menuItem = new JMenuItem("A popup menu item");
    menuItem.addActionListener(this);
    popup.add(menuItem);
    menuItem = new JMenuItem("Another popup menu item");
    menuItem.addActionListener(this);
    popup.add(menuItem);

    //Add listener to components that can bring up popup menus.
    MouseListener popupListener = new PopupListener();
    output.addMouseListener(popupListener);
    menuBar.addMouseListener(popupListener);
...
class PopupListener extends MouseAdapter {
    public void mousePressed(MouseEvent e) {
        maybeShowPopup(e);
    }

    public void mouseReleased(MouseEvent e) {
        maybeShowPopup(e);
    }

    private void maybeShowPopup(MouseEvent e) {
        if (e.isPopupTrigger()) {
            popup.show(e.getComponent(),
                       e.getX(), e.getY());
        }
    }
}

弹出菜单有一些有趣的实现细节。一种是每个菜单都有一个关联的弹出菜单。激活菜单后,它将使用其关联的弹出菜单来显示其菜单项。

另一个细节是,弹出菜单本身使用另一个组件来实现包含菜单项的窗口。根据显示弹出菜单的环境,弹出菜单可能使用轻量级组件(例如JPanel),“中等重量”组件(例如Panel)或重量级窗口(某些东西)来实现其“窗口”。继承自Window)。

轻量级弹出窗口比重量级窗口更有效,但是在 Java SE Platform 6 Update 12 发行之前,如果 GUI 中有任何重量级组件,它们将无法正常工作。具体来说,当轻量级弹出窗口的显示区域与重量级组件的显示区域相交时,将在顶部绘制重量级组件。这是在 6u12 发行之前建议不要混合重量级和轻量级组件的原因之一。如果使用的是较旧的版本,并且绝对需要在 GUI 中使用重量级的组件,则可以调用JPopupMenu.setLightWeightPopupEnabled(false)以禁用轻型弹出窗口。有关在 6u12 和更高版本中混合组件的信息,请参阅混合重量级和轻量级组件

自定义菜单布局

由于菜单由普通的 Swing 组件组成,因此您可以轻松自定义它们。例如,您可以将任何轻量级组件添加到JMenuJMenuBar。并且因为JMenuBar使用BoxLayout,所以您可以仅通过向菜单栏添加不可见的组件来自定义菜单栏的布局。下面是向菜单栏添加glue组件的示例,以便最后一个菜单位于菜单栏的右边缘:

//...create and add some menus...
menuBar.add(Box.createHorizontalGlue());
//...create the rightmost menu...
menuBar.add(rightMenu);

Try this:

这是 MenuGlueDemo 显示的修改后的菜单布局:

MenuGlueDemo

更改菜单外观的另一种方法是更改用于控制菜单的布局 管理 器。例如,您可以将菜单栏的布局 管理 器从默认的左到右BoxLayout更改为GridLayout

Try this:

这是MenuLayoutDemo创建的菜单布局的图片:

MenuLayoutDemo

菜单 API

下表列出了常用的菜单构造函数和方法。使用菜单的 API 分为以下几类:

创建和设置菜单栏

构造函数或方法Purpose
JMenuBar()创建一个菜单栏。
JMenu add(JMenu)将菜单添加到菜单栏的末尾。
void setJMenuBar(JMenuBar)

JMenuBar getJMenuBar()
(在JAppletJDialogJFrameJInternalFrameJRootPane中)
设置或获取appletdialogframeinternal frameroot pane的菜单栏。

创建和填充菜单

构造函数或方法Purpose
JMenu()

JMenu(String)
JMenu(Action)
创建菜单。字符串 指定菜单显示的文本。 Action指定菜单的文本和其他属性(请参见如何使用动作)。
JMenuItem add(JMenuItem)
JMenuItem add(String)
将菜单项添加到菜单的当前末尾。如果参数是字符串,则菜单会自动创建一个JMenuItem对象,该对象显示指定的文本。
void addSeparator()在菜单的当前末尾添加分隔符。
JMenuItem insert(JMenuItem,int)
void insert(String,int)
void insertSeparator(int)
将菜单项或分隔符插入菜单中的指定位置。第一个菜单项位于位置 0,第二个菜单项位于位置 1,依此类推。 JMenuItemString参数的处理方式与相应的add方法相同。
void remove(JMenuItem)
void remove(int)
void removeAll()
从菜单中删除指定的项目。如果参数是整数,则它指定要删除的菜单项的位置。

创建,填充和控制弹出菜单

构造函数或方法Purpose
JPopupMenu()

JPopupMenu(String)
创建一个弹出菜单。可选的字符串 参数指定外观可能会作为弹出窗口的一部分显示的标题。
JMenuItem add(JMenuItem)
JMenuItem add(String)
将菜单项添加到弹出菜单的当前末尾。如果参数是字符串,则菜单会自动创建一个JMenuItem对象,该对象显示指定的文本。
void addSeparator()在弹出菜单的当前末尾添加分隔符。
void insert(Component,int)将菜单项插入菜单中的指定位置。第一个菜单项位于位置 0,第二个菜单项位于位置 1,依此类推。 Component参数指定要添加的菜单项。
void remove(int)
void removeAll()
从菜单中删除指定的项目。如果参数是整数,则它指定要删除的菜单项的位置。
静态 void setLightWeightPopupEnabled(boolean)默认情况下,Swing 使用轻量级组件实现菜单窗口。如果您在 Swing 程序中使用任何重量级组件,都会导致问题,如弹出菜单中所述。 (这是避免使用重量级组件的几种原因之一.)作为一种解决方法,调用JPopupMenu.setLightWeightPopupEnabled(false)
void show(Component,int,int)在指定组件的坐标系中的指定 x,y 位置(由整数参数依次指定)处显示弹出菜单。

实现菜单项

构造函数或方法Purpose
JMenuItem()

JMenuItem(String)
JMenuItem(Icon)
JMenuItem(String, Icon)
JMenuItem(String, int)
JMenuItem(Action)
创建普通菜单项。 icon 参数(如果存在)指定菜单项应显示的图标。同样,字符串 参数指定菜单项应显示的文本。 integer 参数指定要使用的键盘助记符。您可以指定KeyEvent类中定义的任何相关 VK 常量。例如,要指定 A 键,请使用KeyEvent.VK_A
具有Action参数的构造函数设置菜单项的Action,从而从Action初始化菜单项的属性。有关详细信息,请参见如何使用动作
JCheckBoxMenuItem()
JCheckBoxMenuItem(String)
JCheckBoxMenuItem(Icon)
JCheckBoxMenuItem(String, Icon)
JCheckBoxMenuItem(String, boolean)
JCheckBoxMenuItem(String,Icon,boolean)
创建一个外观和作用类似于复选框的菜单项。字符串 参数(如果有)指定菜单项应显示的文本。如果为布尔参数指定true,则最初选择(选中)菜单项。否则,最初不会选择菜单项。
JRadioButtonMenuItem()
JRadioButtonMenuItem(String)
JRadioButtonMenuItem(Icon)
JRadioButtonMenuItem(String, Icon)
JRadioButtonMenuItem(String, boolean)
JRadioButtonMenuItem(Icon, boolean)
JRadioButtonMenuItem(String,Icon,boolean)
创建一个外观和作用类似于单选按钮的菜单项。字符串 参数(如果有)指定菜单项应显示的文本。如果为布尔参数指定true,则最初选择菜单项。否则,最初不会选择菜单项。
void setState(boolean)
boolean getState()
(在JCheckBoxMenuItem中)
设置或获取复选框菜单项的选择状态。
void setEnabled(boolean)如果参数为 true,则启用菜单项。否则,禁用菜单项。
void setMnemonic(int)设置助记符,使键盘可以导航到菜单或菜单项。使用KeyEvent类中定义的 VK 常量之一。
void setAccelerator(KeyStroke)设置用于激活菜单项的加速器。
void setActionCommand(String)设置菜单项执行的操作的名称。
void addActionListener(ActionListener)
void addItemListener(ItemListener)
将事件侦听器添加到菜单项。有关详情,请参见处理菜单项中的事件
void setAction(Action)设置与菜单项关联的Action。有关详情,请参见如何使用动作
前面的许多方法都是从AbstractButton继承的。有关AbstractButton提供的其他有用方法的信息,请参见Button API

使用菜单的示例

在我们的一些示例中使用了菜单。

ExampleWhere DescribedNotes
MenuLookDemo本节(Creating Menus)一个简单的示例,它创建除弹出菜单之外的所有菜单,但不处理菜单项中的事件。
MenuDemo本节(处理菜单项中的事件)将事件处理添加到MenuLookDemo
PopupMenuDemo本节(弹出菜单)将弹出菜单添加到MenuDemo
MenuGlueDemo本节(自定义菜单布局)通过向菜单栏添加不可见的组件来演示影响菜单布局的方法。
MenuLayoutDemo本节(自定义菜单布局)实现在垂直菜单栏中排列的侧向打开菜单。
MenuSelectionManagerDemo向 MenuDemo 添加突出显示检测。要查看此功能,请单击菜单,然后将鼠标移到任何菜单项或子菜单上。每秒一次,文本区域将使用有关当前突出显示的菜单项的信息进行更新,不要与用户final选择的菜单项混淆。此演示使用默认的MenuSelectionManager,它跟踪菜单层次结构的状态。
ActionDemo如何使用动作使用Action对象来实现菜单项,这些菜单项复制了工具栏按钮提供的功能。
Framework调出多个相同的框架,每个框架在其菜单栏中都有一个菜单。
InternalFrameDemo如何使用内部框架使用菜单项创建窗口。

请参阅使用 JavaFX UI 控件:菜单教程,以了解如何在 JavaFX 中创建菜单。