【问题标题】:how to get notified when the window title bar is pressed/clicked按下/单击窗口标题栏时如何获得通知
【发布时间】:2014-11-25 21:19:58
【问题描述】:

我正在使用 JWindow(不是 JPopupMenu)开发自动下拉建议窗口(如 Google)。我的下拉 JWindow 不是可聚焦的,也不是模态的(文本字段需要在用户键入时保持焦点)。

每当用户在下拉菜单之外的任何位置按下鼠标,或者应用失去焦点或被最小化或按下退出键时(基本上就像 JPopupMenu 的行为),我都想关闭下拉菜单。

我可以正常工作除了当用户按下主框架标题栏时我不知道如何获取事件(这会导致主框架位于下拉列表的前面)。 p>

我担心没有事件发生,因为我没有收到这个听众的任何东西:

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);
  }
}

【问题讨论】:

  • 你试过WindowListener.windowActivated()吗?
  • 我相信我的 AWT 事件监听器也应该得到所有的窗口事件,包括 windowActivated。但我会再试一次......(经过测试:不,当我最小化和恢复我的主框架时,我确实收到了 windowActivated 事件,但不仅仅是通过单击窗口标题栏)。
  • 我记得,这是不可能的,或者至少非常困难。参见例如bugs.java.com/bugdatabase/view_bug.do?bug_id=6491619
  • 当您单击包含窗口的本机标题栏或边框时,JComboBox 弹出窗口会关闭,显然 JPopupMenus 也会这样做,因此肯定有办法做到这一点。我在这里问了一个相关的问题:stackoverflow.com/questions/43810491/…

标签: java swing


【解决方案1】:

首先,首先查看Toolkit#addAWTEventListener 的文档

参数:
listener - 事件监听器。
eventMask - 要接收的事件类型的位掩码

eventMask 是您想要接收的事件 ID 的位掩码。好吧,它没有那么壮观,但它的意思是,你应该传递一个你有兴趣收到通知的事件 id 的 or'ed 列表......

现在问题变成了,我们对哪些事件感兴趣以及如何获取 id 的...

嗯,AWTEvent 包含一个事件 id 列表,可与ToolkitAWTEvent.WINDOW_FOCUS_EVENT_MASK 一起使用,甚至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 的更改...

【讨论】:

  • 我使用 Integer.MAX_VALUE 作为监听所有可能事件的懒惰方式。不幸的是,单击窗口标题栏时我根本没有收到任何通知。
  • 我以为我正在监听所有可能的事件,但是当我单击主框架标题栏时,我根本没有收到任何事件。现在我也开始想也许我应该使用 0xFFFFFFFF 而不是 Integer.MAX_VALUE。
  • 您将获得WINDOW_LOST/GAIN_FOCUS 事件的唯一可能事件,这些确实是唯一可能触发的事件...弹出窗口是否可聚焦?
  • 否,弹出的 JWindow 不可聚焦。
  • 嗯,这可能是个问题,如果弹出窗口不可聚焦,父窗口永远不会失去焦点,因此永远不需要重新获得焦点......
【解决方案2】:

如果windowActivated() 不适合您,那么作为最后的手段,您始终可以将您的 JFrame 设置为未装饰并创建您自己的标题栏。它看起来不再像原生标题栏,但您将拥有更多控制权,包括添加鼠标侦听器的能力。

myFrame.setUndecorated(true);

【讨论】:

    【解决方案3】:

    不是我的问题的真正答案,但仍然是一个重要的细节:我忘记将拥有的框架传递给弹出 JWindow 的构造函数。当单击主框架标题栏时,这至少可以防止弹出窗口隐藏在主框架后面。

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多