3

Swing ユーザー インターフェイスを備えたクロスプラットフォーム Java アプリケーションがあります。OS X では、アプリケーションはスクリーン メニュー バーを使用して、よりネイティブなユーザー エクスペリエンスを提供します。

通常、アプリケーションはJFrameドキュメントごとに 1 つ作成します。画面のメニュー バーは、これらすべてのウィンドウで一貫している必要があります。私はそれを行ういくつかの方法を試しましたが、適切ではあるが完全ではない、一貫したパフォーマンスの高いソリューションを 1 つだけ見つけました。他の誰かがより良いアプローチをしている場合に備えて、この情報が他の人に役立つことを願って、この質問を投稿しています。

うまくいかないいくつかのアプローチ:

複数のウィンドウに同じメニュー バーをアタッチする

JMenuBar同じものを複数のJFrameインスタンスに追加しようとしましたが、 Swing は一度に 1 つの JFrame にアタッチされた JMenuBar のみをサポートし、スクリーン メニュー バーとしてもサポートします。

MenuBarではなくAWT でもテストしましたJMenuBarが、同じ現象が発生します。AndMenuBarに比べて多くの制限がJMenuBarあります (例: アイコンがない) ので、JMenuBar.

メニューバーを複製する

JMenuBar一般的な解決策の 1 つは、 for each newのコピーを作成することJFrameです。ただし、これには少なくとも 2 つの問題があります。まず、メニュー バーの同期を維持する必要があります。リスナーを使用してそれを行うことはできますが、OS X プラットフォームを処理するためだけに多くの余分なコードが必要になります。ただし、2 番目のより深刻な問題はパフォーマンスです。数百のメニュー項目を含む複雑なメニュー バーがある場合、メニュー バーの複製は非常に遅くなります。このアプローチにより、新しいウィンドウの表示が数秒遅れることがわかりました。

デフォルトのメニュー バーを使用する

OS X v10.6 Update 1 および 10.5 Update 6の Java で、Apple の Java ライブラリに新しいメソッドが追加されましたApplication.setDefaultMenuBar(JMenuBar)

このメソッドの目的は、no がアクティブなときにメニュー バーを提供することですが、独自のnoがアクティブなJFrameときにデフォルトのメニュー バーも表示します。JFrameJMenuBar

ただし、setDefaultMenuBar機能にはいくつかの大きな問題があります。

  1. アクセラレータが機能しませんすべてのキー押下を自分で処理することで、アプリケーションでこの問題を回避しましたが、それでも残念です。
  2. 2012 年 12 月の時点で、setDefaultMenuBar は Java7 ではまだ使用できませんでした。非推奨またはサポートされていない API の使用は明らかに避けたいと考えています。
  3. 最も重要なのは、呼び出しsetDefaultMenuBar によって JVM が適切にシャットダウンされなくなることです。その後の呼び出しでもsetDefaultMenuBar(null)、必要なリソースは解放されません。

要するに、setDefaultMenuBar安全で堅牢な方法とはまったく思えません。

したがって、問題は次のとおりです。一貫した画面を実装するための最も信頼性が高く、パフォーマンスが高く、互換性のある (OS X のバージョン間で) 方法はJMenuBar何ですか?

4

2 に答える 2

3

私が見つけた十分にうまく機能する解決策は、アプリケーションの各ウィンドウにwindowActivateda を追加してイベントをリッスンすることです。WindowListener次に、新しくアクティブ化したウィンドウJMenuBarを、表示したい唯一のメニュー バーに設定します。

例を次に示します。

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);
  }

}

このアプローチでは、両方のフレームに同じメニュー バーが表示され、両方のフレームがなくなると、明示的に を呼び出す必要なく、JVM が正常に終了しますSystem.exit(int)

残念ながら、このアプローチは完全ではありません。アクティブなウィンドウが変わるたびに、メニュー バーが一時的に消えてしまいます。誰もがより良い方法を知っていますか?

于 2013-04-23T17:48:52.403 に答える