TITLE:JMenuBarのJMenuを折り返し
Posted by terai at 2010-12-27

JMenuBarのJMenuを折り返し

JMenuBarのレイアウトマネージャーを変更して、JMenuを折り返して表示します。
  • category: swing folder: MenuBarLayout title: JMenuBarのJMenuを折り返し tags: [JMenuBar, JMenu, LayoutManager, FlowLayout] author: aterai pubdate: 2010-12-27T11:25:50+09:00 description: JMenuBarのレイアウトマネージャーを変更して、JMenuを折り返して表示します。 image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TRf4-liTfjI/AAAAAAAAAwk/CURxxE6iDqk/s800/MenuBarLayout.png

概要

JMenuBarのレイアウトマネージャーを変更して、JMenuを折り返して表示します。

#screenshot

サンプルコード

#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
JMenuBar menuBar = new JMenuBar();
#spandel
menuBar.setLayout(new FlowLayout(FlowLayout.LEFT,2,2) {
#spanend
#spanadd
menuBar.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2) {
#spanend
  @Override public Dimension preferredLayoutSize(Container target) {
    synchronized (target.getTreeLock()) {
      int targetWidth = target.getSize().width;
      if (targetWidth == 0) targetWidth = Integer.MAX_VALUE;
      targetWidth = targetWidth == 0 ? Integer.MAX_VALUE : targetWidth;
      Insets insets = target.getInsets();
      int hgap = getHgap();
      int vgap = getVgap();
      int maxWidth = targetWidth - (insets.left + insets.right);
      int height   = vgap;
      int rowWidth = hgap, rowHeight = 0;
      int nmembers = target.getComponentCount();
      for(int i = 0; i < nmembers; i++) {
      for (int i = 0; i < nmembers; i++) {
        Component m = target.getComponent(i);
        if(m.isVisible()) {
        if (m.isVisible()) {
          Dimension d = m.getPreferredSize();
          if(rowWidth + d.width > maxWidth) {
          if (rowWidth + d.width > maxWidth) {
            height += rowHeight;
            rowWidth = hgap;
            rowHeight = 0;
          }
          rowWidth += d.width + hgap;
          rowHeight = Math.max(rowHeight, d.height + vgap);
        }
      }
      height += rowHeight + insets.top  + insets.bottom;
      return new Dimension(targetWidth, height);
    }
  }
#spandel
};
#spanend
#spanadd
});
#spanend

解説

上記のサンプルでは、JMenuBar(デフォルトのレイアウトマネージャーはBoxLayout)に、FlowLayoutを継承して折り返しを行うレイアウトマネージャを設定して、JMenuがフレームの幅に収まらない場合は折り返して表示するようにしています。

解説

上記のサンプルでは、JMenuBarFlowLayoutを継承して折り返しを行うLayoutManagerを設定して(JMenuBarのデフォルトLayoutManagerBoxLayout)、内部のJMenuなどがフレームの幅に収まらない場合は折り返して表示しています。
  • - 上記のサンプルでは、BorderLayoutを設定したJPanelにadd(menubar, BorderLayout.NORTH)としてJMenuBarを追加していますが、JFrame#setJMenuBar メソッドを使用した場合、以下のような不具合?があります。
  • JFrameの最大化、最小化で折り返しが更新されない
    • 以下のように、FlowLayout#layoutContainerをオーバーライドすることで回避
  • BorderLayoutを設定したJPanel#add(menubar, BorderLayout.NORTH)としてJMenuBarを追加してJFrame#setJMenuBar(...)メソッドを使用した場合、以下のような不具合が存在する?
    • JFrameの最大化、最小化で折り返しが更新されない
    • 以下のようなWindowStateListenerJFrameに追加し、ContentPanerevalidate()して回避
      #spandel
      //http://tips4java.wordpress.com/2008/11/06/wrap-layout/
      #spanend
      #spandel
      //WrapLayout.java
      #spanend
      #spandel
      //Rob Camick on November 6, 2008
      #spanend
      #spanadd
      frame.addWindowStateListener(new WindowStateListener() {
      #spanend
        @Override public void windowStateChanged(final WindowEvent e) {
          EventQueue.invokeLater(new Runnable() {
            @Override public void run() {
              System.out.println("windowStateChanged");
              JFrame f = (JFrame) e.getWindow();
              ((JComponent) f.getContentPane()).revalidate();
            }
          });
        }
      #spanadd
      });
      #spanend
      #spanadd
      // frame.getContentPane().addComponentListener(new ComponentAdapter() {
      #spanend
      #spanadd
      //   @Override public void componentResized(ComponentEvent e) {
      #spanend
      #spanadd
      //     ((JComponent) e.getSource()).revalidate();
      #spanend
      #spanadd
      //   }
      #spanend
      #spanadd
      // });
      #spanend
      #spanadd
      
    • または、以下のようにFlowLayout#layoutContainerをオーバーライドすることで回避
      #spanend
      #spanadd
      // https://tips4java.wordpress.com/2008/11/06/wrap-layout/
      #spanend
      #spanadd
      // WrapLayout.java
      #spanend
      #spanadd
      // Rob Camick on November 6, 2008
      #spanend
      private Dimension preferredLayoutSize;
      @Override public void layoutContainer(Container target) {
        Dimension size = preferredLayoutSize(target);
        if(size.equals(preferredLayoutSize)) {
        if (size.equals(preferredLayoutSize)) {
          super.layoutContainer(target);
        }else{
        } else {
          preferredLayoutSize = size;
          Container top = target;
          while(!(top instanceof Window) && top.getParent() != null) {
          while (!(top instanceof Window) && top.getParent() != null) {
            top = top.getParent();
          }
          top.validate();
        }
      }
      
  • JFrame#pack()しても、JFrameのサイズが変更されない
    • JFrame#setSize(...)に変更することで回避
    • JFrame#pack()してもJFrameのサイズが変更されない
      • JFrame#setSize(...)に変更することで回避

参考リンク

参考リンク

コメント

コメント