3

我希望以编程方式使菜单上的特定项目被选中并显示为这样,如果按下 Enter,将执行相应的操作。不幸的是,我发现 JMenuItem.setSelected() 和 JPopupMenu.setSelectedItem() 都不是我想要的。基本上我想发生当按下箭头键或鼠标移动到特定项目的空间时发生的事情 - 背景颜色改变,表示选择。我没有编程,它只是发生了。为什么这些 API 不做同样的事情?这让我发疯了。不应该这么难。是否有一些 API 可以满足我的要求?

4

5 回答 5

3

这有点工作:

JMenuItem#setArmed(boolean);

尽管除非您遍历 JMenus 到达那里,否则您看不到它。也许如果你在它上面的每个菜单上调用它?

编辑: 也许您想要菜单项的加速器?请参阅:如何使用菜单:启用键盘操作

于 2013-02-12T18:35:01.420 回答
1

java.awt.Robot可以做 Trick ;)
考虑下面给出的代码:

import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.Robot;
public class JMenuFrame extends JFrame implements ActionListener
{
    JMenuBar bar;
    JMenu menu ;
    String[] items;
    JMenuItem[] menuItems;
    JButton start;
    Robot robot;
    public void prepareAndShowGUI()
    {
        try
        {
            robot = new Robot();    
        }
        catch (Exception ex){}
        bar = new JMenuBar();
        menu = new JMenu("File");
        items =  new String[]{"Open","Save","Save As","Quit"};
        menuItems = new JMenuItem[items.length];
        start = new JButton("Click me");
        for (int i = 0 ; i < items.length ; i++)
        {
            menuItems[i] = new JMenuItem(items[i]);
            menuItems[i].addActionListener(this);
            menu.add(menuItems[i]);
        }
        bar.add(menu);
        setJMenuBar(bar);
        start.addActionListener(this);
        getContentPane().add(start,BorderLayout.SOUTH);
        setPreferredSize(new Dimension(300,400));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent evt)
    {
        if ("Click me".equals(evt.getActionCommand()))
        {
            menu.doClick();
            if (robot!=null)
            {
                for (int i = 0 ; i<=2 ; i++) //Suppose you want to select 3rd MenuItem
                {
                    if (!menuItems[i].isEnabled())
                    {
                        continue;
                    }
                    robot.keyPress(KeyEvent.VK_DOWN);
                    robot.keyRelease(KeyEvent.VK_UP);
                }
            }
        }
        else
        {
            JOptionPane.showMessageDialog(this,evt.getActionCommand()+" is pressed","Information",JOptionPane.INFORMATION_MESSAGE);
        }
    }
    public static void main(String st[])
    {
        SwingUtilities.invokeLater( new Runnable()
        {
            public void run()
            {
                JMenuFrame mf = new JMenuFrame();
                mf.prepareAndShowGUI();
            }
        });
    }
}
于 2013-02-12T19:42:11.410 回答
0

它像罪恶一样丑陋,但是与上面 splungebob 的 setArmed() 答案相关的是完整的解决方案:首先,将菜单设为 MenuKeyListener 并将其添加为 MenuKeyListener 自身。然后:

    public void menuKeyReleased(MenuKeyEvent e) {
    if (e.getModifiers() == 0) {
        switch (e.getKeyCode()) {
        case KeyEvent.VK_ENTER:
        case KeyEvent.VK_SPACE:
            for (MenuElement elem : this.getSubElements()) {
                if (elem instanceof JMenuItem) {
                    JMenuItem item = (JMenuItem) elem;
                    if (item.isArmed()) {
                        Action action = item.getAction();
                        if (action != null) {
                            action.actionPerformed(new ActionEvent(this, 0, null));
                            e.consume();
                            setVisible(false);
                        }
                    }
                }
            }
        }
    }   
}

我不敢相信这是这么困难。在构建以键盘为中心的界面时,Swing 有一定的局限性。

于 2013-02-12T19:37:56.193 回答
0

虽然我不确定我是否同意这些要求(请参阅我在 OP 中的评论),但我仍然想试一试。代码的前半部分只是设置 GUI,以便用户可以创建条件。

- 创建一个包含 3 个项目的菜单,并创建 3 个单独的复选框来控制菜单项的状态。

- 如果在任何时候只启用一个菜单项,请自动展开菜单并“预选”该项目。自动扩展菜单的代码是从 JMenu 中提取的。

- 在菜单中添加一个 MenuKeyListener,以在菜单树展开时捕获用户点击空格键。

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

public class JMenuDemo implements Runnable
{
  private final String[] ACTIONS = new String[]{"Open", "Print", "Close"};

  private JMenu fileMenu;
  private JMenuItem[] menuItems;
  private JCheckBox[] checkBoxes;

  public static void main(String args[])
  {
    SwingUtilities.invokeLater(new JMenuDemo()); 
  }

  public void run()
  {
    JPanel panel = new JPanel(new GridLayout(0,1));
    panel.setBorder(BorderFactory.createTitledBorder("Enabled"));

    menuItems = new JMenuItem[ACTIONS.length];
    checkBoxes = new JCheckBox[ACTIONS.length];

    for (int i = 0; i < ACTIONS.length; i++)
    {
      final int index = i;
      final String action = ACTIONS[i];

      menuItems[i] = new JMenuItem(action);
      menuItems[i].setAccelerator(KeyStroke.getKeyStroke(action.charAt(0),
                                  ActionEvent.ALT_MASK));
      menuItems[i].setMnemonic(action.charAt(0));
      menuItems[i].addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent event)
        {
          System.out.println(action);
        }
      });

      checkBoxes[i] = new JCheckBox(action);
      checkBoxes[i].setSelected(true);
      checkBoxes[i].addItemListener(new ItemListener()
      {
        public void itemStateChanged(ItemEvent event)
        {
          checkBoxChanged(index);
        }
      });
      panel.add(checkBoxes[i]);
    }

    JFrame frame = new JFrame("Demo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setJMenuBar(createJMenuBar());
    frame.add(panel, BorderLayout.SOUTH);
    frame.setSize(300, 400);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  private void checkBoxChanged(int index)
  {
    menuItems[index].setEnabled(checkBoxes[index].isSelected());
    evaluate();
  }

  private JMenuBar createJMenuBar()
  {
    fileMenu = new JMenu("File");
    fileMenu.setMnemonic('F');
    fileMenu.addMenuKeyListener(new MenuKeyListener()
    {
      @Override
      public void menuKeyTyped(MenuKeyEvent event)
      {
        autoClick(event);
      }

      @Override
      public void menuKeyPressed(MenuKeyEvent event) {}

      @Override
      public void menuKeyReleased(MenuKeyEvent event) {}
    });

    for (int i = 0; i < menuItems.length; i++)
    {
      fileMenu.add(menuItems[i]);
    }

    JMenuBar menuBar = new JMenuBar();
    menuBar.add(fileMenu);

    return menuBar;
  }

  private void autoClick(MenuKeyEvent event)
  {
    if (event.getModifiers() == 0 && event.getKeyChar() == KeyEvent.VK_SPACE)
    {
      for (JMenuItem menuItem : menuItems)
      {
        if (menuItem.isArmed())
        {
          menuItem.doClick();
          MenuSelectionManager.defaultManager().setSelectedPath(null);
        }
      }
    }
  }

  private void evaluate()
  {
    JMenuItem onlyOne = null;

    for (JMenuItem menuItem : menuItems)
    {
      menuItem.setArmed(false);

      if (menuItem.isEnabled())
      {
        if (onlyOne == null)
        {
          onlyOne = menuItem;
        }
        else
        {
          onlyOne = null;
          break;
        }
      }
    }

    // Show the path if only one is enabled
    if (onlyOne != null)
    {
      onlyOne.setArmed(true);
      MenuElement me[] = buildMenuElementArray(fileMenu);
      MenuSelectionManager.defaultManager().setSelectedPath(me);
    }
  }

  /*
   * Copied from JMenu
   */
  private MenuElement[] buildMenuElementArray(JMenu leaf)
  {
    Vector<JComponent> elements = new Vector<JComponent>();
    Component current = leaf.getPopupMenu();

    while (true)
    {
      if (current instanceof JPopupMenu)
      {
        JPopupMenu pop = (JPopupMenu) current;
        elements.insertElementAt(pop, 0);
        current = pop.getInvoker();
      }
      else if (current instanceof JMenu)
      {
        JMenu menu = (JMenu) current;
        elements.insertElementAt(menu, 0);
        current = menu.getParent();
      }
      else if (current instanceof JMenuBar)
      {
        JMenuBar bar = (JMenuBar) current;
        elements.insertElementAt(bar, 0);
        MenuElement me[] = new MenuElement[elements.size()];
        elements.copyInto(me);
        return me;
      }
    }
  }
}
于 2013-02-13T15:55:45.663 回答
0
    jMenu1.doClick(); // this open the menu again
于 2015-05-18T14:32:37.287 回答