2009-01-27 58 views
6

在过去,当一个人做了一个JPopupMenu可见它的第一个项目将在默认情况下都被选择:http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html如何选择JPopupMenu中的第一项?

如今的默认行为是弹出菜单中选择没有任何项目。我想创建一个JPopupMenu与一个单一的项目将弹出选中并在鼠标指针下居中。我设法让项目在鼠标中间弹出,但我JMenuItem拒绝渲染,就好像它被选中一样。如果我将鼠标从项目中移出并重新插入,则可以正确选择。

任何想法?

这里是我的测试用例:

import java.awt.Component; 
import java.awt.Point; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 

public class Test extends JFrame 
{ 
    public static void main(String[] args) 
    { 
     JFrame frame = new JFrame(); 
     frame.setSize(800, 600); 
     frame.getContentPane().addMouseListener(new MouseAdapter() 
     { 
      @Override 
      public void mousePressed(MouseEvent e) 
      { 
       if (e.isPopupTrigger()) 
        popupTriggered(e); 
      } 

      @Override 
      public void mouseReleased(MouseEvent e) 
      { 
       if (e.isPopupTrigger()) 
        popupTriggered(e); 
      } 

      private void popupTriggered(MouseEvent e) 
      { 
       JPopupMenu menu = new JPopupMenu(); 
       final JMenuItem item = new JMenuItem("This is a JMenuItem"); 
       menu.add(item); 
       Point point = e.getPoint(); 
       int x = point.x - (item.getPreferredSize().width/2); 
       int y = point.y - (item.getPreferredSize().height/2); 
       menu.show((Component) e.getSource(), x, y); 
      } 
     }); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.setVisible(true); 
    } 
} 
+0

我也发现了一些不一致的行为(请参阅我的更新回答)。你能证实这一点吗? – 2009-01-30 03:20:37

+0

我向Sun报告这是一个错误。我会让你知道他们回信。 – Gili 2009-01-30 19:48:18

+0

以下是相关的错误报告:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6799989 – Gili 2009-02-28 17:37:35

回答

0

如今的默认行为是弹出菜单中选择没有任何项目。

其实,我认为这是正确的行为,至少在Windows中。其他非Java应用程序也这样做。即使菜单中只有一个项目,我认为不值得打破这个惯例。如果您觉得不适合,可以按照sean.bright's answer中的设置选择索引。


所以,我终于尝试一下Java的1.6.0_11的机会,发现了一些不一致的行为:如果在弹出菜单中伸出父框架,该项目会自动选择;如果弹出式菜单完全出现在父框架内,则不会选择任何内容。听起来像一个Swing bug,它至少保证RFE的一致行为。

0

这很奇怪。

我与Windows试了一下,用的Java 1.5.0_08甚至1.6.0_07第一要素是自动选择,因为你希望它是。

所以,我想它1.6.0_11,它不工作了,第一个元素是不是最初选择。 在selectionModel中选择元素似乎没有帮助。

一种解决方法(我并不为此感到骄傲)是在显示弹出式菜单后,使用MouseEvent的坐标自动移动鼠标。也许有人有更好的主意?

import java.awt.AWTException; 
import java.awt.Robot; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 

import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 

public class SelectedPopupMenu extends JFrame { 

    public SelectedPopupMenu() { 
     addMouseListener(new MouseAdapter() { 
      public void mouseClicked(final MouseEvent e) { 
       JPopupMenu popupMenu = new JPopupMenu(); 
       popupMenu.add(new JMenuItem("Test-Item")); 
       popupMenu.add(new JMenuItem("Test-Item-2")); 
       // do not care to really hit the center of the popup 
       popupMenu.show(SelectedPopupMenu.this, e.getX() - 30, e.getY() - 10); 
       try { 
        // shake mouse, so that first element is selected even in Java 1.6.0_11 
        Robot robot = new Robot(); 
        robot.mouseMove(e.getX() + 1, e.getY()); 
        robot.mouseMove(e.getX(), e.getY()); 
       } catch (AWTException ex) { 
        ex.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     JFrame frame = new SelectedPopupMenu(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setSize(800, 600); 
     frame.setVisible(true); 
    } 
} 
6

秘密原来是MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, ...});

import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 
import javax.swing.MenuElement; 
import javax.swing.MenuSelectionManager; 
import javax.swing.SwingUtilities; 

/** 
* Demonstrates programmatic {@link JMenuItem} selection; 
* specifically how to make the first item selected by default 
*/ 
public class TestPopup extends JFrame { 
    public static void main(String[] args) { 
    final JFrame frame = new JFrame("TestPopup"); 
    frame.setSize(640, 480); 
    frame.getContentPane().addMouseListener(new MouseAdapter() { 
     @Override 
     public void mousePressed(MouseEvent e) { 
     if (e.isPopupTrigger()) { 
      popupTriggered(e); 
     } 
     } 
     private void popupTriggered(MouseEvent e) { 
     final JPopupMenu menu = new JPopupMenu(); 
     final JMenuItem item0 = new JMenuItem("JMenuItem 0"); 
     final JMenuItem item1 = new JMenuItem("JMenuItem 1"); 
     menu.add(item0); 
     menu.add(item1); 
     menu.pack(); 
     // use invokeLater or just do this after the menu has been shown 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
      MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0}); 
      } 
     }); 
     int x = (int) ((int) (frame.getSize().width - (menu.getPreferredSize().width/2.))/2.); 
     int y = (int) ((int) (frame.getSize().height - (menu.getPreferredSize().height/2.))/2.); 
     menu.show(frame, x, y); 
     // doesn't work: 
     //item0.setSelected(true); 
     // doesn't work: 
     //menu.getSelectionModel().setSelectedIndex(0); 
     // bingo; see also MenuKeyListener/MenuKeyEvent 
//  MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0}); 
     } 
    }); 
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
    frame.setLocationRelativeTo(null); 
    frame.setVisible(true); 
    } 
} 
1

MenuSelectionManager.defaultManager()确实是一个很好的解决方案,但它不会工作的时候,你会尝试预先选择的JPopupMenu的子菜单(它会隐藏父菜单)。 此外,它弄乱其他键盘导航行为(你不能左键隐藏子菜单等)

不幸的是,有没有这个问题在Swing没有好的解决方案... 我的解决办法是丑陋的,但遗憾的是做这项工作完美:

public static void setMenuSelectedIndex(final JPopupMenu popupMenu, final int index) { 
    SwingUtilities.invokeLater(new Runnable(){public void run() 
    { 
     for (int i=0; i < index+1; i++) { 
      popupMenu.dispatchEvent(new KeyEvent(popupMenu, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_DOWN, '\0')); 
     } 
    }}); 
} 

正如你可以看到的弹出菜单,我基本上模拟“向下”键盘按键事件...

更好的解决方案可能不是Hardcodedly模拟VK_DOWN,而是读取Popup的输入地图,并确定哪个KeyCode的意思是“选择下一个菜单项” - 但我认为我们大多数人会与这种黑客相处...

您可能还想要看看这种方法,一旦它被选中,它就选择一个菜单的项目 它利用了以前的方法d

public static void setSelectedIndexWhenVisible(final JMenu menu, final int index) { 
    menu.getPopupMenu().addPopupMenuListener(new PopupMenuListener() { 
     @Override 
     public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 
      PopupUtils.setMenuSelectedIndex(menu.getPopupMenu(), index); 
      menu.getPopupMenu().removePopupMenuListener(this); 
     } 

     @Override 
     public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 
     } 

     @Override 
     public void popupMenuCanceled(PopupMenuEvent e) { 
     } 
    }); 
} 
相关问题