3

我们最近在应用程序的一部分中附加了一个 GWT MenuBar,用于菜单目的。

基本上我希望当您将鼠标悬停在顶级菜单上时打开子菜单,这很容易做到:

menubar.setAutoOpen(true);

我还想让子菜单在用户鼠标离开子菜单时自动隐藏。理想情况下有某种延迟以防止它突然消失,但我会满足于隐藏。

这似乎不是内置的,GWT 中的 MenuItem 对象直接子类化 UIObject 这意味着没有相对琐碎的 onBrowserEvent() 或附加鼠标侦听器的地方。可能扩展 MenuItem 和下沉/下沉事件会让我添加这种行为,但我不确定这是否是最好的方法。

那么自动隐藏 GWT 子菜单的最佳方法是什么?

谢谢你。

4

4 回答 4

2

这是一个相当完整的解决方案,并不完美,在代码后解释:

public class MyMenuBar extends Composite {

    private class OpenTab implements ScheduledCommand {
        private String wid;

        public OpenTab(String windowId) {
            wid = windowId;
        }

        @Override
        public void execute() {
            WinUtl.newAppTab(wid);
        }
    }

    interface MyMenuBarUiBinder extends UiBinder<Widget, MyMenuBar> {}

    private static MyMenuBarUiBinder uiBinder =
                                GWT.create(MyMenuBarUiBinder.class);

    @UiField MenuBar mainMenu;

    @UiField MenuBar subsMenu;
    @UiField MenuItem subsChoice1;
    @UiField MenuItem subsChoice2;
    @UiField MenuItem subsChoice3;

    @UiField MenuBar svcPrvdrMenu;
    @UiField MenuItem svcPrvdrChoice1;
    @UiField MenuItem svcPrvdrChoice2;

    @UiField MenuBar netMgtMenu;
    @UiField MenuItem netMgtChoice1;

    @UiField MenuBar reportsMenu;
    @UiField MenuItem reportsChoice1;

    @UiField MenuBar auditsMenu;
    @UiField MenuItem auditsChoice1;

    @UiField MenuBar securityMenu;
    @UiField MenuItem securityChoice1;

    @UiField MenuBar helpMenu;
    @UiField MenuItem helpChoice1;

    private boolean subMenuPopped = false;
    private boolean subMenuEntered = false;

    private static Type<MouseOverHandler> OVR_EVT = MouseOverEvent.getType();
    private static Type<MouseOutHandler> OUT_EVT = MouseOutEvent.getType();

    private MouseOverHandler mainOverHandler = new MouseOverHandler() {
        @Override
        public void onMouseOver(MouseOverEvent event) {
            subMenuPopped = true;
        }
    };

    private MouseOutHandler mainOutHandler = new MouseOutHandler() {
        @Override
        public void onMouseOut(MouseOutEvent event) {
            Element e = event.getRelativeElement()
            boolean movedUp = (event.getRelativeY(e) < 0);
            if ((movedUp && subMenuPopped) || subMenuEntered) {
                subMenuPopped = false;
                subMenuEntered = false;
                mainMenu.closeAllChildren(true);
            }
        }
    };

    private MouseOverHandler subOverHandler = new MouseOverHandler() {
        @Override
        public void onMouseOver(MouseOverEvent event) {
            subMenuEntered = true;
        }
    };

    private MouseOutHandler subOutHandler = new MouseOutHandler() {
        @Override
        public void onMouseOut(MouseOutEvent event) {
            subMenuPopped = false;
            subMenuEntered = false;
            mainMenu.closeAllChildren(true);
        }
    };

    public MyMenuBar() {
        initWidget(uiBinder.createAndBindUi(this));

        mainMenu.addStyleName("npac-MenuBar");
        mainMenu.setAutoOpen(true);
        mainMenu.setAnimationEnabled(true);
        mainMenu.setFocusOnHoverEnabled(true);

        subsChoice1.setScheduledCommand(new OpenTab(Names.Wid.NPA));

        mainMenu.addDomHandler(mainOverHandler, OVR_EVT);
        mainMenu.addDomHandler(mainOutHandler, OUT_EVT);

        addHandlers(subsMenu);
        addHandlers(svcPrvdrMenu);
        addHandlers(netMgtMenu);
        addHandlers(reportsMenu);
        addHandlers(auditsMenu);
        addHandlers(securityMenu);
        addHandlers(helpMenu);
    }

    private void addHandlers(MenuBar m) {
        m.addDomHandler(subOverHandler, OVR_EVT);
        m.addDomHandler(subOutHandler, OUT_EVT);
    }
}

这处理了 mouseOver 打开 subMenu,用户然后鼠标向上,关闭 mainMenu(subMenu 关闭)的情况。它不处理鼠标沿对角线向下移动,越过 subMenu 的任一侧(子菜单保持打开状态)当然可以改进,但我刚刚开始工作并想分享 ;-)

于 2013-09-20T23:20:38.080 回答
2

在经历了许多可怕的黑客尝试来实现类似的东西之后,我们编写了自己的级联菜单作为GWT Portlets 框架的一部分。它显示来自 HTML 模板的菜单项和子菜单,如下所示:

<a href="#home">Home</a>
<a href="#submenu1()">Sub Menu 1</a>
<a href="#away">Away</a>

<div id="submenu1">
    <a href="#hello_world">Hello World</a>
    <a href="#free_memory">Free Memory</a>
    <a href="#submenu2()">Sub Menu 2</a>
</div>

<div id="submenu2">
    <a href="#command_demo">Command Demo</a>
    <a href="#command1()">Command1</a>
    <a href="#command2(arg1,arg2)">Command2</a>
</div>

看起来像方法调用的 URL 广播 CommandEvent。其他人像往常一样触发历史令牌更改。查看在线演示以查看实际菜单。

于 2009-09-28T18:53:44.997 回答
0

使用此代码:

public class MenuBarExt extends MenuBar {

public MenuBarExt()
{
    super();
}

@Override
public void onBrowserEvent(Event event)
{
    switch (DOM.eventGetType(event))
    {
        case Event.ONMOUSEOUT:
            closeAllChildren(false);
            break;
        default:
            super.onBrowserEvent(event);
            break;

    } 
    super.onBrowserEvent(event);
}

}
于 2015-03-14T12:35:48.543 回答
0

不需要可怕的黑客攻击或依赖 JAVA 中的 CSS 来实现带有子菜单的 MenuBar 的自动隐藏。我创建了一个完整的 Parent+Children 下拉菜单示例,其中 mouseover 打开和 mouseOut 关闭,并解释了每个部分供其他人使用。

我目睹人们遇到的常见问题是运行 ((JMenu)e.getSource()).doClick(); 在 mouseEntered 上模拟单击到 JMenu 父级之一,但不能简单地添加到 mouseExited 方法,因为 MouseListener 需要附加到子 MenuItems 以及 JMenu 父级。(在对 MenuBar 的正常分配中它不会这样做 - 仅附加到父 JMenu 对象)。

此外,由于试图让 MouseExit 侦听器仅在鼠标离开整个菜单结构(即子菜单下拉菜单)时触发“关闭”方法而出现问题。

以下是从我的实时应用程序中获取的完整工作答案:

我解决鼠标退出菜单关闭的方法是在构造函数的顶部运行一个布尔变量“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() 实例化中识别对象类型很重要,这样您就可以在鼠标悬停时自动打开菜单(在这个例子是 3x JMenu 父母)但也避免了子异常错误,并允许在不尝试监视鼠标位置的情况下干净地识别菜单结构的 mouseOUT。鼠标监听器如下:

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 时处于活动状态的任何选定路径点:

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 中访问哪些值 - 但它确实是一种享受!即使有嵌套菜单 :) 我希望很多人发现这个完整的例子非常有用。

于 2015-01-02T01:26:13.357 回答