【问题标题】:Auto-hide JMenuBar自动隐藏 JMenuBar
【发布时间】:2010-05-26 10:02:05
【问题描述】:

当我运行以下代码时,菜单栏会在光标移动到窗口上部时显示。问题是,当我将光标向上移动以打开菜单但未选择任何内容,然后将光标移出菜单栏区域时,它变得不可见,但菜单的元素仍保留在屏幕上。

我试图实现的是一个“自动隐藏”菜单栏,当鼠标进入 JFrame 中的某个区域时,该菜单栏变得可见。

public class Test extends JFrame {

    public Test() {
        setLayout(new BorderLayout());
        setSize(300, 300);

        JMenuBar mb = new JMenuBar();
        setJMenuBar(mb);
        mb.setVisible(false);


        JMenu menu = new JMenu("File");
        mb.add(menu);

        menu.add(new JMenuItem("Item-1"));
        menu.add(new JMenuItem("Item-2"));

        addMouseMotionListener(new MouseAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                getJMenuBar().setVisible(e.getY() < 50);
            }
        });
    }

    public static void main(String args[]) {
        new Test().setVisible(true);
    }
}

我想我找到了一种解决方法:如果菜单栏可见并且 JFrame 接收到 mousemove 事件,则发送 ESC 键以关闭任何打开的菜单。

 addMouseMotionListener(new MouseAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                if (getJMenuBar().isVisible()) {
                    try {
                        Robot robot = new Robot();
                        robot.keyPress(KeyEvent.VK_ESCAPE);
                    } catch (AWTException ex) {
                    }

                }
                getJMenuBar().setVisible(e.getY() < 50);
            }
        });

此解决方法取决于外观(ESC 键的含义)。 反正对我来说没问题。

【问题讨论】:

  • 我想过尝试在getJMenuBar().isSelected() 上添加条件,但不幸的是,即使您将选择放在另一个组件中,选择模型也不会更新回 false,因此菜单不会之后消失。

标签: java swing menubar


【解决方案1】:

您可以通过检查是否从 JMenuBar 中选择了任何菜单来使其工作:

public void mouseMoved(MouseEvent e) {
    JMenuBar lMenu = getJMenuBar();
    boolean hasSelectedMenu = false;
    for (int i=0 ; i< lMenu.getMenuCount() ; ++i)
    {
        if (lMenu.getMenu(i).isSelected())
        {
            hasSelectedMenu = true;
            break;
        }
    }

    if(!hasSelectedMenu)
        lMenu.setVisible(e.getY() < 50);
}

在这种情况下,只要您单击 JFrame 中的其他位置,它就会消失。

但是,不完全是,因为它只会在 mouseMoved 上更新。我建议您对 mouseClicked 进行相同的检查,以确保它在单击而不移动时消失。

【讨论】:

    【解决方案2】:

    同样有效:

    frame.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                if (menuBar.isSelected()) {
                    try {
                        Robot robot = new Robot();
                        robot.keyPress(KeyEvent.VK_ESCAPE);
                        SingleSelectionModel sm = menuBar.getSelectionModel();
                        sm.clearSelection();
                    } catch (AWTException ex) {
                    }
    
                }
            }
        });
    

    【讨论】:

      【解决方案3】:

      您还可以在窗口中添加一个透明面板区域并将单独的 MouseListener 附加到该区域面板,以便您可以显示一个不可见的菜单!但我的示例中没有包含这个想法。

      以下是从我的实时应用中获取的完全正常工作的 AutoHide Hierarchical MenuBar:

      我解决鼠标退出菜单关闭的方法是在构造函数的顶部运行一个布尔变量“isMouseOut”来跟踪,然后以更OO友好的方式分配MouseListener来跟踪多个MouseIn -当用户与菜单交互时的MouseOut 事件。它调用了一个单独的 menuClear 方法,作用于布尔“isMouseOut”的状态。该类实现 MouseListener。这就是它的完成方式。

      首先创建一个 ArrayList,将所有菜单项添加到该数组中。像这样:

          Font menuFont = new Font("Arial", Font.PLAIN, 12);
          JMenuBar menuBar = new JMenuBar();
          getContentPane().add(menuBar, BorderLayout.NORTH); 
      
      // Array of MenuItems
          ArrayList<JMenuItem> aMenuItms = new ArrayList<JMenuItem>();
          JMenuItem mntmRefresh = new JMenuItem("Refresh");
          JMenuItem mntmNew = new JMenuItem("New");
          JMenuItem mntmNormal = new JMenuItem("Normal");
          JMenuItem mntmMax = new JMenuItem("Max");
          JMenuItem mntmStatus = new JMenuItem("Status");
          JMenuItem mntmFeedback = new JMenuItem("Send Feedback");
          JMenuItem mntmEtsyTWebsite = new JMenuItem("EtsyT website");
          JMenuItem mntmAbout = new JMenuItem("About");
      
          aMenuItms.add(mntmRefresh);
          aMenuItms.add(mntmNew);
          aMenuItms.add(mntmNormal);
          aMenuItms.add(mntmMax);
          aMenuItms.add(mntmStatus);
          aMenuItms.add(mntmFeedback);
          aMenuItms.add(mntmEtsyTWebsite);
          aMenuItms.add(mntmAbout);
      

      然后在此阶段迭代 arrayList,使用 for() 循环添加 MouseListener:

        for (Component c : aMenuItms) {
              if (c instanceof JMenuItem) {
                  c.addMouseListener(ml);
              }
          }
      

      现在为 MenuBar 设置 JMenu 父级:

      // Now set JMenu parents on MenuBar
          final JMenu mnFile = new JMenu("File");
          menuBar.add(mnFile).setFont(menuFont);
          final JMenu mnView = new JMenu("View");
          menuBar.add(mnView).setFont(menuFont);
          final JMenu mnHelp = new JMenu("Help");
          menuBar.add(mnHelp).setFont(menuFont);
      

      然后将下拉 menuItems 子项添加到 JMenu 父项:

      // Now set menuItems as children of JMenu parents
          mnFile.add(mntmRefresh).setFont(menuFont);
          mnFile.add(mntmNew).setFont(menuFont);
          mnView.add(mntmNormal).setFont(menuFont);
          mnView.add(mntmMax).setFont(menuFont);
          mnHelp.add(mntmStatus).setFont(menuFont);
          mnHelp.add(mntmFeedback).setFont(menuFont);
          mnHelp.add(mntmEtsyTWebsite).setFont(menuFont);
          mnHelp.add(mntmAbout).setFont(menuFont);
      

      将 mouseListeners 作为单独的步骤添加到 JMenu 父级:

          for (Component c : menuBar.getComponents()) {
              if (c instanceof JMenu) {
                  c.addMouseListener(ml);
              }
          }
      

      现在子 menuItem 元素都有自己的侦听器,这些侦听器与父 JMenu 元素和 MenuBar 本身是分开的 - 识别 MouseListener() 实例化中的对象类型很重要,这样您就可以自动打开菜单mouseover(在此示例中为 3x JMenu 父级)但也避免了子异常错误,并允许在不尝试监视鼠标位置的情况下干净地识别菜单结构的 mouseOUT。 MouseListener 如下:

      MouseListener ml = new MouseListener() {
              public void mouseClicked(MouseEvent e) {
              }
      
              public void mousePressed(MouseEvent e) {
              }
      
              public void mouseReleased(MouseEvent e) {
              }
      
              public void mouseExited(MouseEvent e) {
                  isMouseOut = true;
                  timerMenuClear();
              }
      
              public void mouseEntered(MouseEvent e) {
                  isMouseOut = false;
                  Object eSource = e.getSource();
                  if(eSource == mnHelp || eSource == mnView || eSource == mnFile){
                      ((JMenu) eSource).doClick();
                  }
              }
          }; 
      

      以上仅模拟鼠标点击到 JMenu 'parents'(本例中为 3x),因为它们是子菜单下拉菜单的触发器。 timerMenuClear() 方法调用 MenuSelectionManager 以清空实际 mouseOUT 时处于活动状态的任何 selectedpath 点:

      public void timerMenuClear(){
          ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(isMouseOut == true){
                    System.out.println("Timer");
                MenuSelectionManager.defaultManager().clearSelectedPath();
                }
            }
        };        
          //Delay timer half a second to ensure real mouseOUT
        Timer timer = new Timer(1000, task); 
        timer.setInitialDelay(500);        
        timer.setRepeats(false);
        timer.start();
      }
      

      我进行了一些测试,监控在开发过程中我可以在 JVM 中访问哪些值 - 但它确实是一种享受!即使是嵌套菜单 :) 我希望很多人觉得这个完整的例子非常有用。

      【讨论】:

        【解决方案4】:

        迟到总比没有好。我建议您使用 setJMenuBar(null) 而不是设置可见性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-12
          • 2016-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-15
          相关资源
          最近更新 更多