我正在使用JWindow(不是JPopupMenu)处理自动下拉建议窗口(如Google)。我的下拉列表JWindow不可聚焦而不是模态(文本字段需要在用户输入时保持焦点)。
我想在用户在下拉列表外的任何地方按下鼠标时关闭下拉列表,或者如果应用程序失去焦点或被最小化或按下了转义键(基本上像JPopupMenu一样)。
我让它工作除了我无法弄清楚如何在用户按下主框标题栏时获取事件(这会导致主框架在下拉列表前面)。
我担心这没有事件,因为我没有收到任何关于这个听众的事情:
Toolkit.getDefaultToolkit().addAWTEventListener(myTestListener, Integer.MAX_VALUE);
无论如何,JPopupMenu如何实现这种行为?
编辑:添加了SSCCE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class SSCCE
{
public static void main(String[] args) throws Exception
{
final JFrame frame = new JFrame();
JButton button = new JButton("open popup");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
openPopup(frame);
}
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void openPopup(JFrame frame)
{
final JWindow popupWindow = new JWindow();
popupWindow.setFocusable(false);
popupWindow.setSize(400, 400);
popupWindow.setLocation(frame.getX() + 200, frame.getY() + 200);
((JComponent) popupWindow.getContentPane()).setBorder(new MatteBorder(1, 1, 1, 1, Color.RED));
popupWindow.setVisible(true);
AWTEventListener awtEventListener = new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent e)
{
System.out.println(e.toString());
if(e instanceof MouseEvent
&& ((MouseEvent) e).getID() == MouseEvent.MOUSE_PRESSED //
|| e instanceof FocusEvent
&& ((FocusEvent) e).getID() == FocusEvent.FOCUS_LOST //
|| e instanceof ComponentEvent
&& ((ComponentEvent) e).getID() == ComponentEvent.COMPONENT_MOVED
&& e.getSource() != popupWindow //
|| e instanceof ComponentEvent && ((ComponentEvent) e).getID() == ComponentEvent.COMPONENT_RESIZED
&& e.getSource() != popupWindow//
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_STATE_CHANGED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_ACTIVATED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_DEACTIVATED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_GAINED_FOCUS //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_LOST_FOCUS //
|| e instanceof KeyEvent && ((KeyEvent) e).getKeyCode() == KeyEvent.VK_ESCAPE //
)
{
popupWindow.setVisible(false);
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
}
};
Toolkit.getDefaultToolkit().addAWTEventListener(awtEventListener, 0xFFFFFFFF);
}
}
答案 0 :(得分:1)
首先,首先看一下Toolkit#addAWTEventListener
参数:
监听器 - 事件监听器。
eventMask - 要接收的事件类型的位掩码
eventMask
是您要接收的事件ID的位掩码。好吧,它不那么引人注目,但它的意思是,你应该传递一个你感兴趣的事件id列表或者已被通知的事件...
现在问题变成了,我们感兴趣的是什么事件以及我们如何获得id ...
好吧,AWTEvent
包含一个与Toolkit
,AWTEvent.WINDOW_FOCUS_EVENT_MASK
配合使用的事件ID列表,甚至可能AWTEvent.FOCUS_EVENT_MASK
可能有用。
Toolkit.getDefaultToolkit().addAWTEventListener(myTestListener,
AWTEvent.WINDOW_FOCUS_EVENT_MASK |
AWTEvent.FOCUS_EVENT_MASK);
例如......
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFocus {
public static void main(String[] args) {
new TestFocus();
}
public TestFocus() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (event instanceof WindowEvent) {
System.out.println("WindowEvent");
WindowEvent evt = (WindowEvent) event;
if (evt.getID() == WindowEvent.WINDOW_GAINED_FOCUS) {
System.out.println("I got you babe");
} else if (evt.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
System.out.println("Don't leave me!");
}
} else if (event instanceof FocusEvent) {
System.out.println("FocusEvent");
}
}
}, AWTEvent.WINDOW_FOCUS_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
这些将告诉您焦点状态发生变化时,您需要检查KeyboardFocusManager#getFocusOwner
和/或KeyboardFocusManager#getGlobalFocusedWindow
以确定您的窗口是否仍有焦点。
同样,您可以尝试使用KeyboardFocusManager#addPropertyChangeListener
并监控对KeyboardFocusManager
的更改...
答案 1 :(得分:0)
如果windowActivated()
不适合您,那么作为最后的手段,您始终可以将JFrame设置为未修饰并创建自己的标题栏。它不再像原生标题栏,但你会有更多的控制权,包括添加鼠标监听器的能力。
myFrame.setUndecorated(true);
答案 2 :(得分:0)
不是我的问题的答案,但仍然是一个重要的细节:我忘了将拥有框架传递给弹出窗口JWindow的构造函数。当点击主框标题栏时,这至少可以防止弹出窗口隐藏在主框架后面。