文档

Java™教程
隐藏目录
如何使用菜单
路径: 使用Swing创建GUI
课程: 使用Swing组件
部分: 如何使用各种组件

如何使用菜单

菜单提供了一种节省空间的方式,让用户选择多个选项之一。用户可以使用其他组件进行一对多选择的包括组合框列表单选按钮微调器工具栏。如果您的任何菜单项执行的操作与另一个菜单项或工具栏按钮重复,则除了阅读本节外,您还应该阅读如何使用操作

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

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

MenuLookDemo

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

菜单组件层次结构

下图显示了与菜单相关的类的继承层次结构:

菜单类的继承层次结构

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

创建菜单

下面的代码创建了在菜单部分开始处显示的菜单。粗体代码行创建并连接菜单对象;其他代码设置或自定义菜单对象。您可以在MenuLookDemo.java中找到整个程序。其他所需文件在示例索引中列出。


试试看: 

因为这段代码没有事件处理,所以菜单除了显示应该有的外,没有其他有用的功能。如果您运行示例,您会注意到尽管缺少自定义事件处理,菜单和子菜单会在应该出现的时候出现,并且当用户选择复选框和单选按钮时,它们会做出适当的响应。

//创建GUI界面:
JMenuBar 菜单栏;
JMenu 菜单, 子菜单;
JMenuItem 菜单项;
JRadioButtonMenuItem 单选按钮菜单项;
JCheckBoxMenuItem 复选框菜单项;

//创建菜单栏。
菜单栏 = new JMenuBar();

//构建第一个菜单。
菜单 = new JMenu("菜单");
菜单.setMnemonic(KeyEvent.VK_A);
菜单.getAccessibleContext().setAccessibleDescription(
        "此程序中唯一有菜单项的菜单");
菜单栏.add(菜单);

//一组菜单项
菜单项 = new JMenuItem("只有文本的菜单项",
                         KeyEvent.VK_T);
菜单项.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_1, ActionEvent.ALT_MASK));
菜单项.getAccessibleContext().setAccessibleDescription(
        "这个并不实际做任何事情");
菜单.add(菜单项);

菜单项 = new JMenuItem("文本和图标",
                         new ImageIcon("images/middle.gif"));
菜单项.setMnemonic(KeyEvent.VK_B);
菜单.add(菜单项);

菜单项 = new JMenuItem(new ImageIcon("images/middle.gif"));
菜单项.setMnemonic(KeyEvent.VK_D);
菜单.add(菜单项);

//一组单选按钮菜单项
菜单.addSeparator();
ButtonGroup group = new ButtonGroup();
单选按钮菜单项 = new JRadioButtonMenuItem("单选按钮菜单项");
单选按钮菜单项.setSelected(true);
单选按钮菜单项.setMnemonic(KeyEvent.VK_R);
group.add(单选按钮菜单项);
菜单.add(单选按钮菜单项);

单选按钮菜单项 = new JRadioButtonMenuItem("另一个");
单选按钮菜单项.setMnemonic(KeyEvent.VK_O);
group.add(单选按钮菜单项);
菜单.add(单选按钮菜单项);

//一组复选框菜单项
菜单.addSeparator();
复选框菜单项 = new JCheckBoxMenuItem("复选框菜单项");
复选框菜单项.setMnemonic(KeyEvent.VK_C);
菜单.add(复选框菜单项);

复选框菜单项 = new JCheckBoxMenuItem("另一个");
复选框菜单项.setMnemonic(KeyEvent.VK_H);
菜单.add(复选框菜单项);

//一个子菜单
菜单.addSeparator();
子菜单 = new JMenu("子菜单");
子菜单.setMnemonic(KeyEvent.VK_S);

菜单项 = new JMenuItem("子菜单中的项");
菜单项.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_2, ActionEvent.ALT_MASK));
子菜单.add(菜单项);

菜单项 = new JMenuItem("另一个项");
子菜单.add(菜单项);
菜单.add(子菜单);

//在菜单栏中构建第二个菜单。
菜单 = new JMenu("另一个菜单");
菜单.setMnemonic(KeyEvent.VK_N);
菜单.getAccessibleContext().setAccessibleDescription(
        "这个菜单什么也不做");
菜单栏.add(菜单);

...
frame.setJMenuBar(菜单栏);

代码示例中,要为 JFrame 设置菜单栏,使用 setJMenuBar 方法。要向 JMenuBar 添加 JMenu,使用 add(JMenu) 方法。要向 JMenu 添加菜单项和子菜单,使用 add(JMenuItem) 方法。


注意: 

菜单项,和其他组件一样,最多只能存在一个容器中。如果尝试将菜单项添加到第二个菜单中,在添加到第二个菜单之前,菜单项将从第一个菜单中移除。如果要实现多个组件执行相同操作的方式,请参阅 使用操作的方法


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

处理菜单项的事件

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


试试看: 
文本区域显示我们的监听器检测到的动作和项目事件。

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

public class MenuDemo ... implements ActionListener,
                                     ItemListener {
    ...
    public MenuDemo() {
        //...对于每个JMenuItem实例:
        menuItem.addActionListener(this);
        ...
        //对于每个JRadioButtonMenuItem:
        rbMenuItem.addActionListener(this);
        ...
        //对于每个JCheckBoxMenuItem:
        cbMenuItem.addItemListener(this);
        ...
    }

    public void actionPerformed(ActionEvent e) {
        //...从动作事件获取信息...
        //...在文本区域中显示它...
    }

    public void itemStateChanged(ItemEvent e) {
        //...从项目事件获取信息...
        //...在文本区域中显示它...
    }

有关处理动作和项目事件的示例,请参见 按钮, 单选按钮复选框部分,以及本节末尾的 示例列表

启用键盘操作

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

助记键是使已经可见的菜单项被选择的键。例如,在 MenuDemo 中,第一个菜单的助记键是 A,它的第二个菜单项的助记键是 B。这意味着当你使用 Java 的外观和感觉运行 MenuDemo 时,按下 Alt 和 A 键会使第一个菜单出现。当第一个菜单可见时,按下 B 键(带有或不带有 Alt)会选择第二个菜单项。菜单项通常通过在菜单项文本中对助记字符的第一次出现进行下划线显示助记键,如下图所示。

B 是此菜单项的助记键字符

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

Alt+2 表示此菜单项的加速键

您可以在构建菜单项时指定助记键,也可以使用 setMnemonic 方法指定。要指定加速键,请使用 setAccelerator 方法。以下是设置助记键和加速键的示例:

// 在构建菜单项时设置助记键:
menuItem = new JMenuItem("一个纯文本的菜单项",
                         KeyEvent.VK_T);

// 在创建后设置助记键:
menuItem.setMnemonic(KeyEvent.VK_T);

// 设置加速键:
menuItem.setAccelerator(KeyStroke.getKeyStroke(
        KeyEvent.VK_T, ActionEvent.ALT_MASK));

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


注意: 

由于弹出菜单(与常规菜单不同)不总是由组件包含,因此弹出菜单项中的加速键只有在弹出菜单可见时才起作用。


弹出菜单

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

引起弹出菜单的确切手势因外观和感觉而异。在Microsoft Windows中,按照约定,当光标位于启用弹出菜单的组件上方时,用户通过释放右键来弹出弹出菜单。在Java外观和感觉中,通常的触发器是按下右键(对于在释放按钮时消失的弹出菜单)或点击它(对于保持打开的弹出菜单)。


试一下: 
//...在声明实例变量的位置:
JPopupMenu popup;

    //...在构建GUI的位置:
    //创建弹出菜单。
    popup = new JPopupMenu();
    menuItem = new JMenuItem("一个弹出菜单项");
    menuItem.addActionListener(this);
    popup.add(menuItem);
    menuItem = new JMenuItem("另一个弹出菜单项");
    menuItem.addActionListener(this);
    popup.add(menuItem);

    //向可以弹出菜单的组件添加监听器。
    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平台6 Update 12版本之前,如果GUI中有任何重型组件,它们在轻量级弹出窗口的显示区域与重型组件的显示区域相交时,重型组件会被绘制在顶部。这是在6u12版本之前我们推荐不要混合使用重型和轻量级组件的原因之一。如果您使用的是旧版本并且确实需要在GUI中使用重型组件,则可以调用JPopupMenu.setLightWeightPopupEnabled(false)禁用轻量级弹出窗口。有关在6u12版本及更高版本中混合组件的信息,请参阅混合重型和轻量级组件

自定义菜单布局

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

//...创建并添加一些菜单...
menuBar.add(Box.createHorizontalGlue());
//...创建最右边的菜单...
menuBar.add(rightMenu);

尝试一下: 

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

MenuGlueDemo

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


试试这个: 

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

MenuLayoutDemo

菜单 API

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

创建和设置菜单栏
构造函数或方法 目的
JMenuBar() 创建一个菜单栏。
JMenu add(JMenu) 将菜单添加到菜单栏的末尾。
void setJMenuBar(JMenuBar)
JMenuBar getJMenuBar()
(在JAppletJDialogJFrameJInternalFrameJRootPane中)
设置或获取JAppletJDialogJFrameJInternalFrameJRootPane的菜单栏。
创建和填充菜单
构造函数或方法 目的
JMenu()
JMenu(String)
JMenu(Action)
创建一个菜单。字符串指定菜单显示的文本。Action指定菜单的文本和其他属性(请参阅如何使用Actions)。
JMenuItem add(JMenuItem)
JMenuItem add(String)
将菜单项添加到菜单的末尾。如果参数是字符串,则菜单会自动创建一个显示指定文本的JMenuItem对象。
void addSeparator() 将分隔符添加到菜单的末尾。
JMenuItem insert(JMenuItem, int)
void insert(String, int)
void insertSeparator(int)
在指定位置插入菜单项或分隔符。第一个菜单项位于位置0,第二个位于位置1,依此类推。JMenuItem和String参数在相应的add方法中的处理方式相同。
void remove(JMenuItem)
void remove(int)
void removeAll()
从菜单中移除指定的项。如果参数是整数,则指定要移除的菜单项的位置。
创建、填充和控制弹出菜单
构造函数或方法 目的
JPopupMenu()
JPopupMenu(String)
创建弹出菜单。可选的字符串参数指定外观和感觉可能作为弹出窗口的一部分显示的标题。
JMenuItem add(JMenuItem)
JMenuItem add(String)
将菜单项添加到弹出菜单的末尾。如果参数是一个字符串,则菜单自动创建一个显示指定文本的JMenuItem对象。
void addSeparator() 在弹出菜单的末尾添加分隔符。
void insert(Component, int) 在指定位置将菜单项插入菜单中。第一个菜单项位于位置0,第二个菜单项位于位置1,依此类推。Component参数指定要添加的菜单项。
void remove(int)
void removeAll()
从菜单中删除指定的项。如果参数是整数,则指定要删除的菜单项的位置。
static void setLightWeightPopupEnabled(boolean) 默认情况下,Swing使用轻量级组件来实现菜单的窗口。如果在Swing程序中使用任何重量级组件,可能会导致问题,如弹出菜单中所述。(这是避免使用重量级组件的几个原因之一。)作为解决方法,请调用JPopupMenu.setLightWeightPopupEnabled(false)
void show(Component, int, int) 在指定组件的坐标系中,以指定的x、y位置(按照整数参数的顺序指定)显示弹出菜单。
实现菜单项JMenuItem()
JMenuItem(String)
JMenuItem(Icon)
JMenuItem(String, Icon)
JMenuItem(String, int)
JMenuItem(Action)

具有Action参数的构造函数设置菜单项的Action,从而使菜单项的属性从Action初始化。有关详细信息,请参阅如何使用Actions。

JCheckBoxMenuItem()
JCheckBoxMenuItem(String)
JCheckBoxMenuItem(Icon)
JCheckBoxMenuItem(String, Icon)
JCheckBoxMenuItem(String, boolean)
JCheckBoxMenuItem(String, Icon, boolean)JRadioButtonMenuItem()
JRadioButtonMenuItem(String)
JRadioButtonMenuItem(Icon)
JRadioButtonMenuItem(String, Icon)
JRadioButtonMenuItem(String, boolean)
JRadioButtonMenuItem(Icon, boolean)
JRadioButtonMenuItem(String, Icon, boolean)void setState(boolean)
boolean getState()
(在JCheckBoxMenuItem中)void setEnabled(boolean)void setMnemonic(int)KeyEventvoid setAccelerator(KeyStroke)void setActionCommand(String)void addActionListener(ActionListener)
void addItemListener(ItemListener)处理菜单项事件void setAction(Action)Action如何使用ActionsAbstractButtonAbstractButton按钮API

使用菜单的示例

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

示例 描述位置 备注
MenuLookDemo 本节 (创建菜单) 一个简单的示例,创建了各种类型的菜单,但不处理菜单项的事件。
MenuDemo 本节 (处理菜单项的事件) MenuLookDemo中添加了事件处理。
PopupMenuDemo 本节 (弹出菜单) MenuDemo中添加了弹出菜单。
MenuGlueDemo 本节 (自定义菜单布局) 通过向菜单栏添加不可见组件来演示影响菜单布局。
MenuLayoutDemo 本节 (自定义菜单布局) 实现了以垂直菜单栏排列的侧向展开菜单。
MenuSelectionManagerDemo 在MenuDemo中添加了高亮检测功能。要查看此功能,请单击菜单,然后将鼠标移动到任何菜单项或子菜单上。每秒钟,文本区域将更新当前高亮菜单项的信息,不要与用户最终选择的菜单项混淆。此演示使用默认的MenuSelectionManager,跟踪菜单层次的状态。
ActionDemo 如何使用动作 使用Action对象来实现通过工具栏按钮提供的功能的菜单项。
Framework 弹出多个具有菜单栏的相同框架。
InternalFrameDemo 如何使用内部框架 使用菜单项创建窗口。

请查看JavaFX UI控件使用教程:菜单,学习如何在JavaFX中创建菜单。


上一页:如何使用列表
下一页:如何使用面板