Java 使OSX屏幕JMenuBar在windows上一致工作的最佳方法是什么?
我有一个带有Swing用户界面的跨平台Java应用程序。在OSX上,应用程序提供了更为本地的用户体验 通常,应用程序会为每个文档创建一个Java 使OSX屏幕JMenuBar在windows上一致工作的最佳方法是什么?,java,macos,swing,menubar,jmenubar,Java,Macos,Swing,Menubar,Jmenubar,我有一个带有Swing用户界面的跨平台Java应用程序。在OSX上,应用程序提供了更为本地的用户体验 通常,应用程序会为每个文档创建一个JFrame。屏幕菜单栏必须在所有这些窗口中保持一致。我尝试了几种方法,但只找到了一种一致且性能良好的解决方案,这是足够的,但并不完美。我发布这个问题,以防其他人有更好的方法,并希望这些信息能帮助其他人 有些方法不起作用: 将同一菜单栏附加到多个窗口 我尝试将相同的JMenuBar添加到多个JFrame实例中,但是,甚至作为屏幕菜单栏 我还使用AWTMenuBa
JFrame
。屏幕菜单栏必须在所有这些窗口中保持一致。我尝试了几种方法,但只找到了一种一致且性能良好的解决方案,这是足够的,但并不完美。我发布这个问题,以防其他人有更好的方法,并希望这些信息能帮助其他人
有些方法不起作用:
将同一菜单栏附加到多个窗口
我尝试将相同的JMenuBar
添加到多个JFrame
实例中,但是,甚至作为屏幕菜单栏
我还使用AWTMenuBar
而不是JMenuBar
进行了测试,但也出现了同样的现象。与JMenuBar
相比,MenuBar
有许多限制(例如,没有图标),因此让我们继续要求使用JMenuBar
克隆菜单栏
一种常见的解决方案是为每个新的JFrame
创建JMenuBar
的副本。然而,这至少有两个问题。首先,必须保持菜单栏同步。虽然您可以使用监听器来执行此操作,但仅仅为了处理OSX平台就需要大量额外的代码。但是,第二个也是更严重的问题是性能:如果您有一个包含数百个菜单项的复杂菜单栏,那么克隆菜单栏的速度非常慢。我们发现这种方法将新窗口的出现延迟了几秒钟
使用默认菜单栏
苹果的Java库中添加了一个新方法:Application.setDefaultMenuBar(JMenuBar)
此方法的规定目的是在无JFrame
处于活动状态时提供一个菜单栏,但当自身无JMenuBar
的JFrame
处于活动状态时,也会显示默认菜单栏
但是,setDefaultMenuBar
功能存在几个主要问题:
setDefaultMenuBar
。即使随后调用setDefaultMenuBar(null)
也无法释放必要的资源setDefaultMenuBar
似乎根本不是一种安全可靠的方法
因此,问题是:实现一致的屏幕
JMenuBar
,最可靠、性能最好、最兼容(跨OS X版本)的方法是什么?我发现效果最好的解决方案是通过在应用程序的每个窗口中添加WindowListener
来监听windowActivated
事件。然后,将新激活的窗口的JMenuBar
设置为我们想要显示的唯一菜单栏
以下是一份:
使用这种方法,两个帧都显示相同的菜单栏,当两个帧都消失时,JVM将干净地退出,而无需显式调用System.exit(int)
不幸的是,这种方法并不完美:每次活动窗口更改时,菜单栏都会短暂消失。有人知道更好的方法吗?您可以利用继承其父级的
JMenuBar
的JDialog
。要使对话框保持非模态,可以使用
在对话框和主propertychangevent
之间进行通信,如建议的那样JFrame
- 并在对话框之间导航
JMenuBar
,并将其附加到每个Swing窗口。你能详细说明一下MVC是如何避开这个问题的吗?@GuillaumePolet:至于使用multipleJFrame
s是一种不好的做法,这个链接的上下文对于它回答的问题非常具体,我认为在这里不适用。无论如何,为了兼容性和可用性,我们的应用程序是按照以前的版本建模的,带有多窗口(即SDI)设计,我们必须保留这个设计。(不过,我们也有MDI实现。)在更广泛的层面上,我不同意使用多个窗口是“糟糕的做法”;就连OSX本身也附带了许多多窗口应用程序:Safari、Terminal、TextEdit、Preview等。@trashgood:正如我所说,我们还有一个MDI UI,它使用JInternalFrame
。但我们仍然需要一个具有多个窗口的SDI版本。但是使用非模态对话框是一个非常有趣的建议;我没有意识到一个JDialog
继承了其父JMenuBar
。不幸的是,这种方法导致所有子对话框在Windows上都缺少任务栏条目,因此,例如,您不能再在窗口之间使用Alt+Tab,这可能不是一个可接受的折衷办法。@ctrueden:interest;在OSX上,命令选项卡在应用程序之间切换,但不在多个帧之间切换,例如;我使用操作
&键绑定在无模式对话框之间切换。抱歉,我之前忽略了这一点。谢谢,我更喜欢这个解决方案,而不是windowActivated
方法,因为菜单栏保持一致,切换活动窗口时没有闪烁或延迟。这种方法应该在acr中广泛应用
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.WindowConstants;
/**
* On OS X, with a screen menu bar, you can "hot-swap" a JMenuBar between
* multiple JFrames when each is activated. However, there is a flash each
* time the active window changes, where the menu bar disappears momentarily.
* But it is a small price to pay to be able to reuse the same menu bar!
*/
public class HotSwapJMenuBarOSX {
public static void main(final String[] args) {
System.setProperty("apple.laf.useScreenMenuBar", "true");
final JMenuBar menuBar = new JMenuBar();
final JMenu file = new JMenu("File");
menuBar.add(file);
final JMenuItem fileNew = new JMenuItem("New");
file.add(fileNew);
final JFrame frame1 = new JFrame("First");
frame1.getContentPane().add(new JButton("First"));
frame1.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final JFrame frame2 = new JFrame("Second");
frame2.getContentPane().add(new JButton("Second"));
frame2.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// hot-swap the menu bar to newly activated windows
final WindowListener listener = new WindowAdapter() {
@Override
public void windowActivated(WindowEvent e) {
((JFrame) e.getWindow()).setJMenuBar(menuBar);
}
};
frame1.addWindowListener(listener);
frame2.addWindowListener(listener);
final int offsetX = 200, offsetY = 50;
frame1.pack();
frame1.setLocation(offsetX, offsetY);
frame1.setVisible(true);
frame2.pack();
frame2.setLocation(frame1.getWidth() + offsetX + 10, offsetY);
frame2.setVisible(true);
}
}