2016-11-14 126 views
0

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

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”菜单项,只有感动将鼠标放入它。由于鼠标点击,菜单没有关闭。

我该如何做JMenu所做的 - 在不关闭菜单的其余部分的情况下显示JPopupMenu?

+0

也许你可以尝试用JMenu的替代JMenuItem的,一旦你确定它确实确实有子菜单。 – camickr

+0

我认为像这样的一个新成语会让用户感到困惑,并且最好让菜单条目始终是一个JMenuItem,它会显示一个显示动态确定选项的模式对话框(可能是一个JOptionPane)。 – VGR

+0

谢谢,camickr和VGR,为您提供了解决方法的建议。即使我必须使用解决方法,我也确实想知道为什么显示弹出菜单会导致菜单关闭。很明显,它可以在不关闭菜单的情况下完成,因为JMenu可以做到这一点。 – Vine

回答

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