0

如何让 Java 7 应用程序的菜单栏位于屏幕顶部(在 Mac 上),并且键盘快捷键也可以正常工作?

我有一个带有 Swing 用户界面的 Java 应用程序。许多菜单都有键盘等效项,这是必不可少的。

与系统相关的内容很少,但在 Mac OS X 上,菜单栏应该出现在屏幕顶部而不是每个窗口上,所以我设置了apple.laf.useScreenMenuBar.

这在 Java 6 上运行良好,但在 Java 7(上周发布!)上编译和运行相同的代码会导致键盘快捷键执行两次菜单操作。例如,在附加的代码中,Command ⌘+O打开两个文件对话框而不是一个。(其他键盘快捷键也有两次作用,但有时您必须移动窗口才能看到它们的作用。)

如果我不设置apple.laf.useScreenMenuBar,键盘问题就会消失,如果我必须这样做,我会这样做,但我的 Mac 用户会不高兴。我真的很想将菜单栏放在正确的位置,并且键盘快捷键可以正常工作。


系统: 2010 年末 MacBook Pro 上的 Mac OS 10.7.3 (Lion)

Java 7:
Java 版本“1.7.0_04”
Java(TM) SE 运行时环境(构建 1.7.0_04-b21)
Java HotSpot(TM) 64 位服务器 VM(构建 23.0-b21,混合模式)

Java 6:
Java 版本“1.6.0_31”
Java(TM) SE 运行时环境(构建 1.6.0_31-b04-415-11M3635)
Java HotSpot(TM) 64 位服务器 VM(构建 20.6-b01-415,混合模式)


我看过的地方:

关于为什么apple.laf.useScreenMenuBar应该摆脱的讨论 ——我完全赞成,但它似乎没有发生。

一个关于不使用mrj.version来检测你在 Mac 上的讨论 ——不直接相关,但听起来很有希望。

对于附加代码的长度(148 行),我深表歉意,但我的 Swing 编码非常过时。它应该在没有任何特殊标志或设置的情况下从命令行编译和运行。

import javax.swing.*;
import java.awt.Toolkit;
import java.awt.*;
import java.awt.event.*;

/**
 * Shows that using the single screen-top menu bar on a Mac with Java 7
 * causes keyboard shortcuts to act twice.
 *
 * To see the problem(on a Mac -- running OS X 10.7.3 in my case):
 *   1) compile on either Java 6 or Java 7
 *   2) run on Java 7
 *   3) give the command-O shortcut
 * You will see two file dialogues.
 *
 *      -- J. Clarke, May 2012
 */

public class MenuBug {

    private static void go(String[] args) {

        // Comment out the following line to fix the problem;
        // leave it active to see the problem.
        // It doesn't help to ...
        // ... put the line into a static block.
        // ... put the line right after the setLookAndFeel call.
        // ... put the line before after the setLookAndFeel call.
        System.setProperty("apple.laf.useScreenMenuBar", "true");

        MainWindow mainWindow = new MainWindow();
    }

    public static void main(final String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception e) {
            JOptionPane.showMessageDialog(null,
                    e + " while loading look and feel",
                    "MenuBug error", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                go(args);
            }
        });
    }
}

class MainWindow extends JFrame {

    MainWindow() {
        super ("Main Window");

        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        addWindowListener (new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    dispose();
                    System.exit(0);
                }
            });

        JMenuBar menuBar = createMenuBar();
        setJMenuBar(menuBar);

        pack();
        setSize(350,300);
        setVisible(true);
    }

    private JMenuBar createMenuBar() {
        JMenuBar mBar = new JMenuBar();
        JMenu menu = new JMenu("File");
        String[] menuItemNames = new String[] {"New", "Open...", "Other"};
        for (int i = 0; i < menuItemNames.length; i++) {
            String miName = menuItemNames[i];
            JMenuItem mi = new JMenuItem(miName);
            mi.setActionCommand(miName);
            linkMenuItemToAction(mi);
            menu.add(mi);
        }
        mBar.add(menu);
        return mBar;
    }

    /**
     * Create an Action for menuItem, and make sure the action and the menu
     * item know about each other; where appropriate, add keyboard equivalents.
     * @param menuItem  The menu item to be linked to an action.
     */
    private void linkMenuItemToAction(JMenuItem menuItem) {
        final int META_MASK =
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
        Action a = null;

        String miName = menuItem.getActionCommand();
        if (miName.equals ("New")) {
            a = new NewAction();
            menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
                    META_MASK));
        }
        else if (miName.equals ("Open...")) {
            a = new OpenAction();
            menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
                    META_MASK));
        }
        else if (miName.equals ("Other")) {
            a = new OtherAction();
            menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
                    META_MASK));
        }

        menuItem.setEnabled(a.isEnabled());
        menuItem.addActionListener(a);
    }

    private class NewAction extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            new MainWindow();
        }
    }

    private void makeDialog() {
        String dialogTitle = "Please choose a file to open";
        FileDialog fileDialog = new FileDialog(this, dialogTitle,
                FileDialog.LOAD);

        fileDialog.setVisible(true);
        String fileName = fileDialog.getFile();
    }

    private class OpenAction extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            makeDialog();
        }
    }

    private class OtherAction extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null,
                    "an example message",
                    "not really an error", JOptionPane.ERROR_MESSAGE);
        }
    }
}
4

2 回答 2

4

我正在回答我自己的问题 - 有点。正如对原文的评论中所指出的,Java 1.7u10 的问题消失了。

于 2012-12-12T21:02:28.280 回答
0

看起来这个问题仍然存在,但现在它可以在 1.7_21 的 mac 上使用 fn + backSpace (delete) 重现。

我使用了与上面相同的示例,只是添加了文本字段。选择文本字段中的部分文本并按删除(fn+退格键)

在 linkMenuItemToAction 方法中将 KeyStroke 更改为“DELETE”

else if (miName.equals ("Other")) 
{
 a = new OtherAction();
 menuItem.setAccelerator(KeyStroke.getKeyStroke("DELETE"));
}

并添加:

JTextField textField = new JTextField(10);
textField.setText("Long long long long long long long text");
add(textField, BorderLayout.PAGE_START);

到 MainWindow 构造函数。

于 2013-05-27T06:08:08.713 回答