JMenuItem.setSelected()不会改变所选项目的外观?

时间:2013-02-12 18:14:03

标签: java swing jmenuitem jpopupmenu

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

5 个答案:

答案 0 :(得分:2)

这种方式有用:

JMenuItem#setArmed(boolean);

虽然你没有看到它,除非你穿过JMenus到达那里。也许如果你在上面的每个菜单上都叫它?

修改 也许你想要一个菜单​​项的加速器? 请参阅:How To Use Menus: Enabling Keyboard Operation

答案 1 :(得分: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();
            }
        });
    }
}

答案 2 :(得分: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有一定的局限性。

答案 3 :(得分: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;
      }
    }
  }
}

答案 4 :(得分:0)

    jMenu1.doClick(); // this open the menu again