2010-03-11 17 views
7

我需要一个附带下拉式样菜单的JButton。所以我拿了一个JPopupMenu并按照你在下面的代码中看到的方式将它附加到JButton。它所需要做的是这样的:显示/隐藏JButton中的JPopupMenu; FocusListener不工作?

  • 显示弹出点击时
  • 隐藏它,如果点击一次
  • 隐藏它,如果一个项目在弹出窗口上选择
  • 隐藏它,如果用户点击了其他位置在屏幕

这4周工作上的事情,但我使用,因为布尔标志,如果用户点击了其他位置或选择一个项目,我必须在按钮上点击两次就说明前再起。这就是为什么我试图添加一个FocusListener(这绝对没有响应)来解决这个问题,并在这些情况下设置标志为false。

编辑:在回答中后一次尝试...

这里有听众:(它在扩展JButton的一类,所以第二个侦听器是将JButton)

// Show popup on left click. 
menu.addFocusListener(new FocusListener() { 
@Override 
public void focusLost(FocusEvent e) { 
    System.out.println("LOST FOCUS"); 
    isShowingPopup = false; 
} 

@Override 
public void focusGained(FocusEvent e) { 
    System.out.println("GAINED FOCUS"); 
} 
}); 

addActionListener(new ActionListener() { 
@Override 
public void actionPerformed(ActionEvent e) { 
    System.out.println("isShowingPopup: " + isShowingPopup); 
    if (isShowingPopup) { 
    isShowingPopup = false; 
    } else { 
    Component c = (Component) e.getSource(); 
    menu.show(c, -1, c.getHeight()); 
    isShowingPopup = true; 
    } 
} 
}); 

现在我一直在为这种方式而战。如果有人能够给我一些关于这个问题的线索,那会很棒!

谢谢!

代码:

public class Button extends JButton { 

    // Icon. 
    private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png"); 

    // Unit popup menu. 
    private final JPopupMenu menu; 

    // Is the popup showing or not? 
    private boolean isShowingPopup = false; 

    public Button(int height) { 
     super(ARROW_SOUTH); 
     menu = new JPopupMenu(); // menu is populated somewhere else 

     // FocusListener on the JPopupMenu 
     menu.addFocusListener(new FocusListener() { 
      @Override 
      public void focusLost(FocusEvent e) { 
       System.out.println("LOST FOCUS"); 
       isShowingPopup = false; 
      } 

      @Override 
      public void focusGained(FocusEvent e) { 
       System.out.println("GAINED FOCUS"); 
      } 
     }); 

     // ComponentListener on the JPopupMenu 
     menu.addComponentListener(new ComponentListener() { 
      @Override 
      public void componentShown(ComponentEvent e) { 
       System.out.println("SHOWN"); 
      } 

      @Override 
      public void componentResized(ComponentEvent e) { 
       System.out.println("RESIZED"); 
      } 

      @Override 
      public void componentMoved(ComponentEvent e) { 
       System.out.println("MOVED"); 
      } 

      @Override 
      public void componentHidden(ComponentEvent e) { 
       System.out.println("HIDDEN"); 
      } 
     }); 

     // ActionListener on the JButton 
     addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       System.out.println("isShowingPopup: " + isShowingPopup); 
       if (isShowingPopup) { 
        menu.requestFocus(); 
        isShowingPopup = false; 
       } else { 
        Component c = (Component) e.getSource(); 
        menu.show(c, -1, c.getHeight()); 
        isShowingPopup = true; 
       } 
      } 
     }); 

     // Skip when navigating with TAB. 
     setFocusable(true); // Was false first and should be false in the end. 

     menu.setFocusable(true); 
    } 

} 
+0

所以,我的主要问题是,focusGained()和focusLost()永远不会被触发,即使我继续使弹出窗口出现并消失。 – Joanis 2010-03-11 02:09:55

回答

1

下面是另一种方法,即使不是很优雅,也不算太差,而且据我所知,它可以工作。首先,在顶部,我添加了第二个布尔值,称为showPopup

FocusListener有如下:

menu.addFocusListener(new FocusListener() { 
     @Override 
     public void focusLost(FocusEvent e) { 
      System.out.println("LOST FOCUS"); 
      isShowingPopup = false; 
     } 

     @Override 
     public void focusGained(FocusEvent e) { 
      System.out.println("GAINED FOCUS"); 
      isShowingPopup = true; 
     } 
    }); 

isShowingPopup布尔没有得到改变其他地方 - 如果它获得焦点,它假定它表示,如果它失去焦点,它假定它不是”吨。

接下来,按钮上的ActionListener不同的是:

addActionListener(new ActionListener() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      System.out.println("isShowingPopup: " + isShowingPopup); 
      if (showPopup) { 
       Component c = (Component) e.getSource(); 
       menu.show(c, -1, c.getHeight()); 
       menu.requestFocus(); 
      } else { 
       showPopup = true; 
      } 
     } 
    }); 

现在到了真正的新位。这是在按钮上MouseListener

addMouseListener(new MouseAdapter() { 
     @Override 
     public void mousePressed(MouseEvent e) { 
      System.out.println("ispopup?: " + isShowingPopup); 
      if (isShowingPopup) { 
       showPopup = false; 
      } 
     } 

     @Override 
     public void mouseReleased(MouseEvent e) { 
      showPopup = true; 
     } 
    }); 

基本上,mousePressed被之前的菜单失去焦点时调用,所以isShowingPopup反映按下按钮之前是否被显示的弹出窗口。然后,如果菜单在那里,我们只需将showPopup设置为false,以便actionPerformed方法在被调用后(在放开鼠标后)不显示菜单。

这在所有情况下均表现如预期,但只有一种:如果菜单显示并且用户在按钮上按下鼠标,但在其外部释放它,actionPerformed从未被调用过。这意味着showPopup保持为假,并且菜单在下次按下按钮时不显示。为了解决这个问题,mouseReleased方法重置showPopup。据我所知,mouseReleased方法在actionPerformed之后被调用。

我随身携带了一下按钮,做了所有我能想到的按钮,并且按预期工作。但是,我不能100%确定事件总是以相同的顺序发生。

最终,我认为这至少值得尝试。

+0

哇,我不知道为什么FocusListener现在工作(我也称为requestFocus()!)...但我只是测试了这一切,它似乎完美的工作!这正是缺少的!干得好!非常感谢你! – Joanis 2010-03-12 13:25:55

+1

我认为问题出在你调用requestFocus()的地方 - 我在menu.show()之后调用它,这使得它在显示之后得到焦点,而如果菜单已经被触发显示“if(isShowingPopup)...”,这使得它试图在错误的时间聚焦。 – 2010-03-12 15:04:01

+0

不会做测试,因为它现在可以工作,所以你可能是对的!再次感谢。 – Joanis 2010-03-12 15:39:04

1

您可以使用JPopupMenu.isVisible(),而不是你的布尔变量来检查弹出菜单的当前状态。

+1

我一开始试图这样做,但问题是这样的: 只要在弹出窗口可见时单击按钮(或其他地方),弹出窗口就会立即自动关闭。所以无论如何,isVisible()都会返回false。 这个问题(显然)也适用于isFocusOwner()和isShowing()。 – Joanis 2010-03-11 03:31:06

0

嗯,我不能确定没有看到你所有的代码,但是弹出窗口有没有可能真正得到焦点呢?我之前在Swing中没有把注意力放在正确的位置上,所以它可能是罪魁祸首。尝试在菜单上调用setFocusable(true),然后在出现菜单时调用requestFocus()

+0

刚试过;不工作。我会在一秒钟后发布代码。 – Joanis 2010-03-11 14:10:55

1

您是否尝试过加入ComponentListenerJPopupMenu,让你知道什么时候它已经显示和隐藏(并相应更新您isShowingPopup标志)?我不确定倾听焦点变化是否是正确的方法。

+0

这似乎是一个非常好的主意,但就像FocusListener一样,ComponentListener没有响应(当然,只有一次:当第一次调用“调整大小”时显示弹出窗口)。我在每个方法中都放了一个println(“”)调用,并且没有任何内容会被打印出来。我将发布完整的代码。 – Joanis 2010-03-11 14:11:33

1

你需要的是一个PopupMenuListener的:

 menu.addPopupMenuListener(new PopupMenuListener() { 

      @Override 
      public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) { 

      } 

      @Override 
      public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) { 
       System.out.println("MENU INVIS"); 
       isShowingPopup = false;  
      } 

      @Override 
      public void popupMenuCanceled(PopupMenuEvent arg0) { 
       System.out.println("MENU CANCELLED"); 
       isShowingPopup = false;      
      } 
     }); 

我这个插入到你的代码,并验证了它的工作原理。

+0

感谢您的意见。我已经尝试了这种方法...这解决了上面讨论的问题,但创建了一个新的问题。有了这个,我们不能关闭按钮上的弹出式按钮,因为无论开始时它是关闭还是打开,它都会打开。 – Joanis 2010-03-11 18:21:13

+0

当弹出式菜单变得可见时,您可以修改按钮上的动作侦听器,以便它不会打开弹出窗口(即只删除侦听器)。然后,当菜单变得不可见时,将其设置回来(将侦听器添加回去)。 – Dave 2010-03-11 18:32:13

+0

你是对的。由于JPopupMenu将所有其他鼠标事件合并到PopupMenuEvent中,因此我不确定是否有方法“正确”执行此操作。这里是一个(BIG)黑客:在每个popupMenuWillBecomeInvisible事件中保存System.currentTimeMillis(),然后在actionPerformed中执行“if(isShowingPopup ||(System.currentTimeMillis() - savedTime)<100),然后显示...好吧,我并不是建议你这样保留它,但是如果它必须这样工作的话......我可以想到的唯一的其他事情就是编写你自己的JPopupMenu实现,并以你想要的方式处理鼠标事件。 – 2010-03-11 18:45:18

3

下面是我刚刚提出的Amber Shah的“大黑客”建议的一个变种。没有isShowingPopup标志...

它不是防弹的,但它工作得很好,直到有人进来用一个令人难以置信的慢点击关闭弹出窗口(或非常快的第二次点击重新打开它...)。

public class Button extends JButton { 

// Icon. 
private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png"); 

// Popup menu. 
private final JPopupMenu menu; 

// Last time the popup closed. 
private long timeLastShown = 0; 

public Button(int height) { 
    super(ARROW_SOUTH); 
    menu = new JPopupMenu(); // Populated somewhere else. 

    // Show and hide popup on left click. 
    menu.addPopupMenuListener(new PopupMenuListener() { 
    @Override 
    public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) { 
    timeLastShown = System.currentTimeMillis(); 
    } 
    @Override public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {} 
    @Override public void popupMenuCanceled(PopupMenuEvent arg0) {} 
    }); 
    addActionListener(new ActionListener() { 
    @Override 
    public void actionPerformed(ActionEvent e) { 
    if ((System.currentTimeMillis() - timeLastShown) > 300) { 
    Component c = (Component) e.getSource(); 
    menu.show(c, -1, c.getHeight()); 
    } 
    } 
    }); 

    // Skip when navigating with TAB. 
    setFocusable(false); 
} 

} 

正如我在评论说,这不是最好的解决方法,但它是可怕的简单,它的情况下,98%的作品。

欢迎指教!

0

我试过Tikhon Jelvis的答案(介绍focusListener和mouseListener的智能组合)。它在Linux上不适用于我(Java7/gtk)。 :-(

阅读http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29有写着“请注意,使用这种方法是不鼓励,因为其行为是与平台相关的。”

这可能是因为与Java7改变监听器调用的顺序或将其与改变GTK vs Windows。我不会推荐这个解决方案,如果你想成为平台独立。

顺便说一句:我创建了一个新的帐户在stackoverflow给这个提示。似乎我不允许评论他的答案(因为声誉),但似乎我有一个编辑它的按钮,这个stackoverflow是一个非常有趣的事情。:-)