我们最近将一个GWT MenuBar附加到我们的应用程序的一部分,以及菜单用途。
基本上我希望在鼠标悬停在顶级菜单上时打开子菜单,这很容易做到:
menubar.setAutoOpen(true);
我还希望当用户的鼠标离开子菜单时,子菜单会自动隐藏。理想情况下会有一些延迟,以防止它突然消失,但我只会满足于隐藏。
这似乎没有内置,并且GWT中的MenuItem对象直接将UIObject子类化,这意味着没有相对简单的onBrowserEvent()或某处附加鼠标侦听器。可能会扩展MenuItem和沉没/解链事件会让我添加这种行为,但我不确定这是否是最好的方法。
那么自动隐藏GWT子菜单的最佳方法是什么?
谢谢。
答案 0 :(得分:2)
经过非常可怕的黑客攻击尝试实现类似的东西后,我们编写了自己的cascading menu作为GWT Portlets framework的一部分。它显示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。其他人触发正常的历史令牌更改。请查看online demo以查看正在运行的菜单。
答案 1 :(得分: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的任何一侧(子菜单保持打开状态) 当然可以改进,但我只是让它工作,想分享; - )
答案 2 :(得分:0)
在JAVA中不需要可怕的黑客攻击或依赖CSS来实现带有子菜单的MenuBar的自动隐藏。我创建了一个完整工作的Parent + Children下拉菜单示例,其中包含mouseover打开和mouseOut关闭,每个部分的解释供其他人使用。
我亲眼目睹的常见问题是运行((JMenu)e.getSource())。doClick();在mouseEntered上模拟了单击其中一个JMenu父项,但不能简单地添加到mouseExited方法,因为MouseListener需要附加到子MenuItems以及JMenu父项。 (它在正常分配给MenuBar时没有做 - 只附加到父JMenu对象)。
此外,由于尝试让MouseExit侦听器触发&#34;关闭&#34;而出现问题。方法仅当鼠标离开整个菜单结构时(即子菜单下拉菜单)。
以下是从我的实时应用中获得的完整解决方案:
我解决鼠标输出关闭菜单的方法是运行一个布尔变量&#34; isMouseOut&#34;在构造函数的顶部进行跟踪,然后以更友好的方式分配MouseListener,以便在用户与菜单交互时跟踪多个MouseIn-MouseOut事件。它调用一个单独的menuClear方法作用于布尔值&#34; isMouseOut&#34;的状态。该类实现了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);
然后将下拉菜单项目子项添加到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&#39;父母&#39; (在此示例中为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在开发过程中可以访问的值 - 但是它有效!即使有嵌套菜单:)我希望很多人觉得这个完整的例子非常有用。
答案 3 :(得分: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);
}
}