动态创建Java Swing子菜单

时间:2016-11-14 01:12:02

标签: java swing submenu

我知道如何使用JMenu创建Java Swing子菜单。当我们将鼠标悬停在JMenu对象上时,它会显示一个显示子菜单项的JPopupMenu,如下所示:

使用JMenu的子菜单

https://i.stack.imgur.com/K6yz8.png

我的问题是,在我的应用程序中,确定哪些菜单元素将具有子菜单是昂贵的。我不想提前确定特定的菜单元素应该是JMenu还是JMenuItem。我想让每个元素都成为JMenuItem,并且只有当用户通过例如将鼠标悬停在菜单项上时才会显示它的子菜单。像这样:

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

public class Menu2 extends JFrame
{
  public Menu2()
  {
    super("Menu2");
    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    JMenu mItems = new JMenu("Items");
    menuBar.add(mItems);
    mItems.add(new JMI("A"));
    mItems.add(new JMI("B"));
    mItems.add(new JMI("C"));
    JLabel stuff = new JLabel("Other stuff");
    stuff.setPreferredSize(new Dimension(200,200));
    getContentPane().add(stuff);
    pack();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
  }

  private class JMI extends JMenuItem
    implements MouseListener
  {
    public JPopupMenu childrenPopup = null;

    public JMI(String label)
    {
      super(label);
      addMouseListener(this);
    }

    // MouseListener

    public void mouseClicked(MouseEvent ev) {}
    public void mouseEntered(MouseEvent ev)
    {
      // Show a submenu for item "B" only.
      // In real life we'd want a Timer to delay showing the submenu
      // until we are sure the user is hovering the mouse.
      // For simplicity I've omitted it.

      if (getText().equals("B")) {
        if (childrenPopup == null) {
          childrenPopup = new JPopupMenu();
          // Expensive processing to determine submenu elements...
          childrenPopup.add("D");
          childrenPopup.add("E");
        }
        // Display the submenu
        childrenPopup.show(this,getWidth(),0);
      }
    }
    public void mouseExited(MouseEvent ev) {}
    public void mousePressed(MouseEvent ev) {}
    public void mouseReleased(MouseEvent ev) {}
  }


  public static void main(String[] args)
    throws Exception
  {
    new Menu2().setVisible(true);
  }
}

唯一的问题是,当我显示手动创建的JPopupMenu时,菜单的其余部分将关闭。结果显示看起来不像早期的显示,而是像这样:

手动显示子菜单

https://i.stack.imgur.com/cqV7q.png

请注意,我没有<&b>点击&#34; B&#34;菜单项,仅将鼠标移入其中。由于鼠标单击,菜单未关闭。

如何做JMenu的工作 - 显示JPopupMenu而不关闭菜单的其余部分?

1 个答案:

答案 0 :(得分:0)

我暂时决定采用的方法是扩展JMenu JMenuItem并将此类型用于我的菜单元素的所有。但是我 不会填充这些元素(昂贵的步骤)直到用户 通过将鼠标悬停来请求它。

为避免使用JMenu的箭头图标使菜单混乱 通常显示(在这种情况下可能会产生误导),我使用a technique described by Stackoverflow's MadProgrammer在静态工厂方法中实例化无箭头JMenu。自从我 创建无箭头JMenu后恢复箭头图标属性, 在其他地方创建的普通JMenu实例仍会显示箭头。

某些菜单元素需要执行操作并关闭菜单, 就像JMenuItem一样。 JMenu通常不响应鼠标 点击,所以我在MouseListener中执行点击操作。

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

public class Menu3 extends JFrame
{
  public Menu3()
  {
    super("Menu3");
    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    JMenu mItems = new JMenu("Items");
    menuBar.add(mItems);
    mItems.add(JM.create("A"));
    mItems.add(JM.create("B"));
    mItems.add(JM.create("C"));
    JLabel stuff = new JLabel("Other stuff");
    stuff.setPreferredSize(new Dimension(200,200));
    getContentPane().add(stuff);
    pack();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
  }

  private static class JM extends JMenu
    implements MouseListener
  {
    private static final String ARROW_ICON_KEY = "Menu.arrowIcon";

    private boolean populated = false; // Submenu already populated?

    protected JM(String label)
    {
      super(label);
      addMouseListener(this);
    }

    // This static factory method returns a JM without an arrow icon.

    public static JM create(String label)
    {
      UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults();
      Object savedArrowIcon = uiDefaults.get(ARROW_ICON_KEY);
      uiDefaults.put(ARROW_ICON_KEY,null);
      JM newJM = new JM(label);
      uiDefaults.put(ARROW_ICON_KEY,savedArrowIcon);
      return newJM;
    }

    // MouseListener

    public void mouseClicked(MouseEvent ev)
    {
      // Since some menu elements need to execute actions and a JMenu
      // doesn't normally respond to mouse clicks, we execute click
      // actions here.  In real life we'll probably fire some event
      // for which an EventListener can listen.  For illustrative
      // purposes we'll just write out a trace message.

      System.err.println("Executing "+getText());
      MenuSelectionManager.defaultManager().clearSelectedPath();
    }
    public void mouseEntered(MouseEvent ev)
    {
      // In real life we'd want a Timer to delay showing the submenu
      // until we are sure the user is "hovering" the mouse.
      // For simplicity I've omitted it.

      // Populate this submenu only once
      if (!populated) {
        // For purposes of example, show a submenu for item "B" only.
        if (getText().equals("B")) {
          // Expensive processing...
          add(create("D"));
          add(create("E"));
        }
        populated = true;
      }
    }
    public void mouseExited(MouseEvent ev) {}
    public void mousePressed(MouseEvent ev) {}
    public void mouseReleased(MouseEvent ev) {}
  }

  public static void main(String[] args)
    throws Exception
  {
    new Menu3().setVisible(true);
  }
}

结果按照我想要的方式运作:

Menu3 with open menu