Java JDialog在mac上混淆了JMenuBar

时间:2012-10-27 06:18:03

标签: java macos swing jdialog jmenubar

我对JMenuBar有一些问题,我似乎无法弄明白。

我将首先解决问题:该程序由JFrame,JDialog和JMenuBar组成。最初,您将看到顶部带有JMenuBar的JFrame。但在某些时候,JDialog会弹出用户可以填写一些文本字段的位置。我遇到的问题是,一旦焦点转到JDialog,JMenuBar就会消失。我想要的是JMenuBar始终保持在屏幕的顶部,除非整个程序不在焦点。这是2个截图,在第一个屏幕截图中,JFrame被选中,而另一个选择了JDialog。

enter image description here

enter image description here

所以我真正想要的是,当焦点在JFrame上时,我只想看到JMenuBar,而不是只看到JMenuBar。由于JDialogs不能将JMenuBar放在顶部,就像JFrame一样,我决定不再拥有多个JMenuBars,而只是一直应该可见的JMenuBars。

最后,我将给出尽可能小(并且仍在工作)的代码的一部分,并且还包含问题:

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;

/**
 * @author Guus Leijsten
 * @created Oct 27, 2012
 */
public class MenuBarProblem extends JFrame {
    public MenuBarProblem() {
        super("Frame");
        this.setMinimumSize(new Dimension(270, 200));
        this.setPreferredSize(new Dimension(800, 530));
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        JRootPane root = this.getRootPane();
    //Menu
        JMenu fileMenu = new JMenu("File");
        JMenuItem file_exit = new JMenuItem("Exit");
        file_exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        file_exit.setToolTipText("Exit application");
        file_exit.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        fileMenu.add(file_exit);

        JMenuBar menu = new JMenuBar();
        menu.add(fileMenu);
        root.setJMenuBar(menu);

        this.setVisible(true);

        JDialog d = new JDialog(this, "Dialog");
        d.setSize(200, 100);
        d.setLocation(0, (int)root.getContentPane().getLocationOnScreen().getY());
        d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        d.setVisible(true);
    }

    public static void main(String[] args) {
        String os = System.getProperty("os.name").toLowerCase();
        if(os.indexOf("mac") >= 0) {
            System.setProperty("apple.laf.useScreenMenuBar", "true");
        }
        new MenuBarProblem();
    }
}

如果我能够表示尊敬,我认为问题出在JRootPane的部分。但我们会看到;)

是否有其他人遇到过这个问题,并设法解决了这个问题,或者是否有人想要试一试?

提前致谢!


添加内容:

在下面的例子中,我将展示一个为游戏提供一些功能的版本。

这是我正在制作的节目: enter image description here 第二个图像显示右侧菜单未对接的状态。 enter image description here 显然JMenuBar应该仍然可见并且可操作,因为没有它,程序的许多功能将被禁用。

此时我开始认为,当对话框(未对接的菜单)未对接并且专注于对象时,JMenuBar不可能保持可见。

我知道JDialog上的JMenuBar不能采用mac osx风格(屏幕顶部),所以有没有其他技术可以用于取消停靠,这确实给了我一个mac osx风格的JMenuBar?

2 个答案:

答案 0 :(得分:2)

解决这个问题的一个关键,即双关语,是让key binding分享一个共同的menu action,如下所示。请注意菜单项,对话框的内容和(其他多余的)按钮都可以使用相同的Action实例。一些补充说明:

  • 赞赏使用getMenuShortcutKeyMask()

  • 应在event dispatch thread (EDT)构建和操作Swing GUI对象。

  • 系统属性应在开始EDT之前设置

  • 的几何图形已知之后,使对话框的setLocation()相对于框架

  • 常见的Mac惯用法使用以下谓词:

    if (System.getProperty("os.name").startsWith("Mac OS X") {…}
    
  • 另请参阅此example

  • 要在对话框本身中进行本地使用,还请考虑JToolBar

MenuBarProblem with menu

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 * @see https://stackoverflow.com/a/13100894/230513
 */
public class MenuBarProblem extends JFrame {

    private static final int MASK =
        Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    private static final String exitName = "Exit";
    private static final KeyStroke exitKey =
        KeyStroke.getKeyStroke(KeyEvent.VK_W, MASK);
    private final ExitAction exitAction = new ExitAction(exitName);

    public MenuBarProblem() {
        super("Frame");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        JMenu fileMenu = new JMenu("File");
        JMenuItem fileExit = new JMenuItem(exitAction);
        fileMenu.add(fileExit);
        JMenuBar menu = new JMenuBar();
        menu.add(fileMenu);

        JDialog d = new JDialog(this, "Dialog");
        JPanel p = new JPanel();
        p.getInputMap().put(exitKey, exitName);
        p.getActionMap().put(exitName, exitAction);
        p.add(new JButton(exitAction));
        d.add(p);
        d.pack();
        d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        this.setJMenuBar(menu);
        this.pack();
        this.setSize(new Dimension(320, 240));
        this.setLocationByPlatform(true);
        this.setVisible(true);
        d.setLocation(this.getRootPane().getContentPane().getLocationOnScreen());
        d.setVisible(true);
    }

    private static class ExitAction extends AbstractAction {


        public ExitAction(String name) {
            super(name);
            this.putValue(Action.MNEMONIC_KEY, exitKey.getKeyCode());
            this.putValue(Action.ACCELERATOR_KEY, exitKey);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    public static void main(String[] args) {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MenuBarProblem();
            }
        });
    }
}

答案 1 :(得分:0)

解决!

使用带有setAlwaysOnTop(true)的JFrame,可以在焦点发生变化时获得JMenuBar所需的效果。