单击SplitMenuItem的行为与任何其他常规JMenuItem相同。但是一个SplitMenuItem在其右侧有一个额外的向下箭头,当单击时在这个箭头上,将出现一个类似JComboBox的下拉菜单,您可以选择进一步的操作。此功能通常通过应用子菜单。SplitMenuItem的想法源于这样一种情况一个动作在频率上占主导地位,其他一些动作与这个主要动作有关行动很少被选择。SplitMenuItem的主要动作是始终可直接访问,因为打开子菜单已成为不必要的。我使用了@MadProgrammer的SplitButton代码,并将其改编为JMenuItem,但仍然不可接受的是:
- 鼠标悬停在 上的项目不高亮显示
- 取消或关闭项是强制性的,因为弹出窗口不会在"外部"上关闭。点击。
- 显示弹出窗口时,父菜单功能正常。
我试过了:
- 删除所有监听器 附加一个FocusListener (focusLost)到SplitMenuItem的父类(这是一个JPopupMenu)。
为了使MCV运行,我包含了SplitMenuItem类的大部分。但是除了将JButton更改为JMenuItem之外,我实际上只修改了一个参数构造函数和showPopupMenu()方法。
/**
* A JMenuItem that has an additional section with an arrow icon on the right
* that when clicked shows a JPopupMenu that is positioned flush with the
* menu item.
*
* Credit:
* An adaptation of SplitButton finalized by MadProgrammer and DUDSS.
* https://stackoverflow.com/questions/36352707/actions-inside-of-another-action-like-netbeans
* Applying code from Darryl Burke's StayOpenMenuItem.
* https://tips4java.wordpress.com/2010/09/12/keeping-menus-open/
*
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
public class SplitMenuItem extends JMenuItem {
private int separatorSpacing = 4;
private int splitWidth = 22;
private int arrowSize = 8;
private boolean onSplit;
private Rectangle splitRectangle;
private boolean alwaysDropDown;
private Color arrowColor = Color.BLACK;
private Color disabledArrowColor = Color.GRAY;
private Image image;
private MouseHandler mouseHandler;
private JPopupMenu jpopupMenu;
// From Darryl Burke's StayOpenMenuItem.
private static MenuElement[] path;
{
getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (getModel().isArmed() && isShowing()) {
path = MenuSelectionManager.defaultManager().getSelectedPath();
}
}
});
}
public SplitMenuItem(JMenu parent) {
super();
addMouseMotionListener(getMouseHandler());
addMouseListener(getMouseHandler());
// Default for no "default" action...
setAlwaysDropDown(true);
// The next line prevents the JMenu's item list/JPopupMenu to become
// invisible when clicking on SplitMenuItem's arrow.
setUI(new StayOpenMenuItemUI());
InputMap im = getInputMap(WHEN_FOCUSED);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
"PopupMenu.close");
ActionMap am = getActionMap();
am.put("PopupMenu.close", new ClosePopupAction());
JPopupMenu parentPop= parent.getPopupMenu();
/* Never fired.
parentPop.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
DBG.p("ParentMenu lost focus");
}
});
*/
parentPop.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuCanceled(PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// This method is called before any actionPerformed in child-popup.
Timer t = new javax.swing.Timer(0, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (jpopupMenu.isVisible())
MenuSelectionManager.defaultManager().setSelectedPath(path);
}
});
t.setRepeats(false);
t.start();
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
});
}
public SplitMenuItem(JMenu parent, String text) {
this(parent);
setText(text);
}
public SplitMenuItem(JMenu parent, String text, JPopupMenu popup) {
this(parent);
setText(text);
setPopupMenu(popup);
}
@Override
public void addActionListener(ActionListener l) {
if (l != null) {
setAlwaysDropDown(false);
}
super.addActionListener(l);
}
protected void closePopupMenu() {
getPopupMenu().setVisible(false);
}
@Override
protected void fireActionPerformed(ActionEvent event) {
// This is a little bit of a nasty trick. Basically this is where
// we try and decide if the menuItems "default" action should
// be fired or not. We don't want it firing if the menuItem
// is in "options only" mode or the user clicked on the
// "drop down arrow".
if (onSplit || isAlwaysDropDown()) {
showPopupMenu();
} else {
super.fireActionPerformed(event);
}
}
/**
* Gets the image to be drawn in the split part. If no is set, a new image
* is created with the triangle.
*
* @return image
*/
public Image getImage() {
if (image == null) {
Graphics2D g = null;
BufferedImage img = new BufferedImage(arrowSize, arrowSize,
BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) img.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.setColor(jpopupMenu != null ? arrowColor : disabledArrowColor);
//this creates a triangle facing right >
g.fillPolygon(new int[]{0, 0, arrowSize/2},
new int[]{0, arrowSize, arrowSize/2}, 3);
g.dispose();
//rotate it to face downwards
img = rotate(img, 90);
BufferedImage dimg = new BufferedImage(img.getWidth(),
img.getHeight(), BufferedImage.TYPE_INT_ARGB);
g = (Graphics2D) dimg.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(img, null, 0, 0);
g.dispose();
for (int i = 0; i < dimg.getHeight(); i++) {
for (int j = 0; j < dimg.getWidth(); j++) {
if (dimg.getRGB(j, i) == Color.WHITE.getRGB()) {
dimg.setRGB(j, i, 0x8F1C1C);
}
}
}
image = Toolkit.getDefaultToolkit().createImage(dimg.getSource());
}
return image;
}
@Override
public Insets getInsets() {
Insets insets = (Insets) super.getInsets().clone();
insets.right += splitWidth;
return insets;
}
@Override
public Insets getInsets(Insets insets) {
Insets insets1 = getInsets();
insets.left = insets1.left;
insets.right = insets1.right;
insets.bottom = insets1.bottom;
insets.top = insets1.top;
return insets1;
}
protected MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
protected int getOptionsCount() {
return getPopupMenu().getComponentCount();
}
/**
* Returns the menuItems popup menu.
*
* @return
*/
public JPopupMenu getPopupMenu() {
if (jpopupMenu == null) {
jpopupMenu = new JPopupMenu();
}
return jpopupMenu;
}
/**
*Show the dropdown menu, if attached, even if the menuItem part is clicked.
*
* @return true if alwaysDropdown, false otherwise.
*/
public boolean isAlwaysDropDown() {
return alwaysDropDown;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//Graphics gClone = g.create();//EDIT: Hervé Guillaume
Color oldColor = g.getColor();
splitRectangle = new Rectangle(getWidth() - splitWidth, 0, splitWidth,
getHeight());
g.translate(splitRectangle.x, splitRectangle.y);
int mh = getHeight() / 2;
int mw = splitWidth / 2;
g.drawImage(getImage(), mw-arrowSize/2, mh+2 - arrowSize/2, null);
if (!alwaysDropDown) {
if (getModel().isRollover() || isFocusable()) {
g.setColor(UIManager.getLookAndFeelDefaults()
.getColor("MenuItem.background"));
g.drawLine(1, separatorSpacing + 2, 1,
getHeight() - separatorSpacing - 2);
g.setColor(UIManager.getLookAndFeelDefaults()
.getColor("MenuItem.shadow"));
g.drawLine(2, separatorSpacing + 2, 2,
getHeight() - separatorSpacing - 2);
}
}
g.setColor(oldColor);
g.translate(-splitRectangle.x, -splitRectangle.y);
}
/**
* Rotates the given image with the specified angle.
*
* @param img image to rotate
* @param angle angle of rotation
* @return rotated image
*/
private BufferedImage rotate(BufferedImage img, int angle) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = dimg = new BufferedImage(w, h, img.getType());
Graphics2D g = dimg.createGraphics();
g.rotate(Math.toRadians(angle), w / 2, h / 2);
g.drawImage(img, null, 0, 0);
return dimg;
}
/**
*Show the dropdown menu, if attached, even if the menuItem part is clicked.
*
* If true, this will prevent the menuItem from raising any actionPerformed
* events for itself.
*
* @param value true to show the attached dropdown even if the menuItem part
* is clicked, false otherwise
*/
public void setAlwaysDropDown(boolean value) {
if (alwaysDropDown != value) {
this.alwaysDropDown = value;
firePropertyChange("alwaysDropDown", !alwaysDropDown,
alwaysDropDown);
}
}
public void setPopupMenu(JPopupMenu popup) {
jpopupMenu = popup;
this.setComponentPopupMenu(popup);
}
protected void showPopupMenu() {
if (getOptionsCount() > 0) {
JPopupMenu popup = getPopupMenu();
Point p= getLocationOnScreen();
popup.setLocation(p.x+getWidth() - popup.getPreferredSize().width,
p.y+getHeight());
popup.setVisible(true);
// Must be showing on the screen to determine its location.
// popup.show(this, (getWidth() - popup.getWidth()), getHeight());
}
}
/*
private JMenu getMenu() {
JMenu menu = null;
while (menu == null) {
JPopupMenu popup = (JPopupMenu)this.getParent();
JMenuItem item = (JMenuItem)popup.getInvoker();
if (!(item.getParent() instanceof JPopupMenu)) menu = (JMenu)item;
}
return menu;
}
*/
protected class ClosePopupAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
closePopupMenu();
}
}
protected class MouseHandler extends MouseAdapter {
@Override
public void mouseExited(MouseEvent e) {
onSplit = false;
repaint(splitRectangle);
}
@Override
public void mouseMoved(MouseEvent e) {
if (splitRectangle.contains(e.getPoint())) {
onSplit = true;
} else {
onSplit = false;
}
repaint(splitRectangle);
}
}
}
/**************************************************************************/
import javax.swing.*;
import javax.swing.plaf.basic.*;
class StayOpenMenuItemUI extends BasicMenuItemUI {
@Override
protected void doClick(MenuSelectionManager msm) {
menuItem.doClick(0);
}
}
/**************************************************************************/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SplitMenuItemTest extends JFrame {
public static final long serialVersionUID = 100L;
JMenuItem exitItem, welcomeItem;
SplitMenuItem splitItem;
public SplitMenuItemTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 240);
setLocationRelativeTo(null);
JMenuBar menuBar= new JMenuBar();
setJMenuBar(menuBar);
JMenu menu= new JMenu("A menu");
menuBar.add(menu);
welcomeItem= new JMenuItem("Welcome");
ActListener actListener= new ActListener();
welcomeItem.addActionListener(actListener);
menu.add(welcomeItem);
JPopupMenu popup= createPopupForItem();
splitItem= new SplitMenuItem(menu, "Most often this", popup);
splitItem.addActionListener(e -> {
JOptionPane.showMessageDialog(SplitMenuItemTest.this,
"The usual action of this menuItem will be performed.");
});
menu.add(splitItem);
exitItem= new JMenuItem("Exit");
exitItem.addActionListener(actListener);
menu.add(exitItem);
setVisible(true);
}
static public void main(String args[]) {
EventQueue.invokeLater(SplitMenuItemTest::new);
}
private JPopupMenu createPopupForItem() {
JPopupMenu popup= new JPopupMenu();
JMenuItem seldomItem= popup.add("Seldomly used");
seldomItem.addActionListener(e -> {
System.out.println(seldomItem.getText());
popup.setVisible(false);
});
JMenuItem rareTaskItem= popup.add("Rare task");
rareTaskItem.addActionListener(e -> {
System.out.println(rareTaskItem.getText());
popup.setVisible(false);
});
popup.addSeparator();
JMenuItem cancelItem= popup.add("Cancel"); // Mandatory JMenuItem.
cancelItem.addActionListener(e -> {
popup.setVisible(false);
});
return popup;
}
public class ActListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Object obj= e.getSource();
if (obj==exitItem)
System.exit(0);
else if (obj==welcomeItem)
System.out.println("Welcome");
else
System.out.println("SplitItem was clicked.");
}
}
}
如何使用带有jmenu派生类的子菜单,该类以水平线呈现(也可以用文本"More actions…"代替)之类的)?
那么你的导航问题应该消失了,而外观和感觉仍然是相当相同的。