动态创建 Java 个 Swing 子菜单
Dynamically create Java Swing submenus
我知道如何使用 JMenu 创建 Java Swing 子菜单。当我们将鼠标悬停在 JMenu 对象上时,它会显示一个显示子菜单项的 JPopupMenu,如下所示:
使用 JMenu 的子菜单
我的问题是,在我的应用程序中,确定哪些菜单元素将具有子菜单的成本很高。我不想提前确定特定菜单元素应该是 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 时,菜单的其余部分将关闭。结果显示看起来不像之前的显示,而是像这样:
手动显示子菜单
请注意,我没有在 "B" 菜单项上 单击 ,只是将鼠标移入其中。由于单击鼠标,菜单没有关闭。
我怎样才能像 JMenu 那样做 -- 显示 JPopupMenu 而不关闭菜单的其余部分?
我暂时决定的方法是扩展 JMenu
JMenuItem 并将此类型用于 all 我的菜单元素。但是我
不会 填充 这些元素(昂贵的步骤),直到用户
通过悬停鼠标请求它。
为了避免使用 JMenu 的箭头图标使菜单混乱
通常显示(在这种情况下可能会产生误导),我使用 a technique described by Whosebug'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
我知道如何使用 JMenu 创建 Java Swing 子菜单。当我们将鼠标悬停在 JMenu 对象上时,它会显示一个显示子菜单项的 JPopupMenu,如下所示:
使用 JMenu 的子菜单
我的问题是,在我的应用程序中,确定哪些菜单元素将具有子菜单的成本很高。我不想提前确定特定菜单元素应该是 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 时,菜单的其余部分将关闭。结果显示看起来不像之前的显示,而是像这样:
手动显示子菜单
请注意,我没有在 "B" 菜单项上 单击 ,只是将鼠标移入其中。由于单击鼠标,菜单没有关闭。
我怎样才能像 JMenu 那样做 -- 显示 JPopupMenu 而不关闭菜单的其余部分?
我暂时决定的方法是扩展 JMenu JMenuItem 并将此类型用于 all 我的菜单元素。但是我 不会 填充 这些元素(昂贵的步骤),直到用户 通过悬停鼠标请求它。
为了避免使用 JMenu 的箭头图标使菜单混乱 通常显示(在这种情况下可能会产生误导),我使用 a technique described by Whosebug'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