Java 单击其他位置时,如何隐藏Swing弹出窗口

Java 单击其他位置时,如何隐藏Swing弹出窗口,java,swing,popup,Java,Swing,Popup,我有一个弹出窗口,当用户点击按钮时显示。我想在发生以下任何事件时隐藏弹出窗口: 用户单击应用程序中的其他位置。(例如背景面板) 用户将应用程序最小化 JPOppMenu具有这种行为,但我需要的不仅仅是JMenuItems。以下代码块是演示当前用法的简化图示 import java.awt.*; import java.awt.event.ActionEvent; import javax.swing.*; public class PopupTester extends JFrame {

我有一个弹出窗口,当用户点击按钮时显示。我想在发生以下任何事件时隐藏弹出窗口:

  • 用户单击应用程序中的其他位置。(例如背景面板)
  • 用户将应用程序最小化
  • JPOppMenu具有这种行为,但我需要的不仅仅是JMenuItems。以下代码块是演示当前用法的简化图示

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import javax.swing.*;
    
    public class PopupTester extends JFrame {
      public static void main(String[] args) {
        final PopupTester popupTester = new PopupTester();
        popupTester.setLayout(new FlowLayout());
        popupTester.setSize(300, 100);
        popupTester.add(new JButton("Click Me") {
          @Override
          protected void fireActionPerformed(ActionEvent event) {
            Point location = getLocationOnScreen();
              int y = (int) (location.getY() + getHeight());
              int x = (int) location.getX();
              JLabel myComponent = new JLabel("Howdy");
              Popup popup = PopupFactory.getSharedInstance().getPopup(popupTester, myComponent, x, y);
              popup.show();
            }
          });
          popupTester.add(new JButton("No Click Me"));
          popupTester.setVisible(true);
          popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    }
    
    当有人单击背景面板时,您可以添加到背景面板并隐藏弹出窗口

    要对应用程序最小化做出反应,请使用附加到


    等等,等等。可能看起来很乏味,但肯定会起作用。

    正如pajton在之前的评论中指出的那样,弹出窗口并不是一个听众可以轻松绑定的组件。但是,正如其文档所述,“Popup的实现负责创建和维护自己的组件,以便向用户呈现[其主题]。”

    因此,在使用它作为表示机制时,您的弹出窗口必须以实际的Swing组件的形式显示。让它自己注册到该组件。当组件失去焦点时,将其自身隐藏

    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.Point;
    import java.awt.event.ActionEvent;
    import java.awt.event.WindowEvent;
    import java.awt.event.WindowFocusListener;
    import javax.swing.JButton;
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.Popup;
    
    public class PopupTester extends JFrame {
        private static class MessagePopup extends Popup
            implements WindowFocusListener
        {
            private final JDialog dialog;
    
            public MessagePopup(Frame base, String message) {
                super();
                dialog = new JOptionPane().createDialog( base, "Message" );
                dialog.setModal( false );
                dialog.setContentPane( new JLabel( message ) );
            }
            @Override public void show() {
                dialog.addWindowFocusListener( this );
                dialog.setVisible( true );
            }
            @Override public void hide() {
                dialog.setVisible( false );
                dialog.removeWindowFocusListener( this );
            }
            public void windowGainedFocus( WindowEvent e ) {
                // NO-OP
            }
    
            public void windowLostFocus( WindowEvent e ) {
                hide();
            }
        }
    
        public static void main(String[] args) {
        final PopupTester popupTester = new PopupTester();
        popupTester.setLayout(new FlowLayout());
        popupTester.setSize(300, 100);
        popupTester.add(new JButton("Click Me") {
          @Override
          protected void fireActionPerformed(ActionEvent event) {
            Point location = getLocationOnScreen();
              MessagePopup popup = new MessagePopup( popupTester, "Howdy" );
              popup.show();
            }
          });
          popupTester.add(new JButton("No Click Me"));
          popupTester.setVisible(true);
          popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    }
    

    使用JPOppMenu。您可以向其添加任何组件,而不仅仅是菜单项。

    您可以向弹出窗口添加FocusListener,并在其失去焦点时进行处理。但是,当焦点丢失是由于其他应用程序引起时(新窗口出现在前台,您切换虚拟桌面等),这会给您带来一些麻烦


    但是,也许您(a)知道这种情况不可能发生,或者(b)希望在这种情况下关闭弹出窗口。无论如何,基于焦点的方法可能仍然对您感兴趣。

    感谢pajton和Noel Ang为我指明了正确的方向!这是我最终得到的解决方案。我只是把它包括在这里,以便其他人可以从中受益

    我最终选择了JWindow,因为它没有窗口装饰,但有焦点事件

    import java.awt.*;
    import java.awt.event.*;
    
    import javax.swing.*;
    
    public class PopupTester extends JFrame {
      private static class MessagePopup extends Popup implements WindowFocusListener {
        private final JWindow dialog;
    
        public MessagePopup(Frame base, JLabel component, int x, int y) {
          super();
          dialog = new JWindow(base);
          dialog.setFocusable(true);
          dialog.setLocation(x, y);
          dialog.setContentPane(component);
          component.setBorder(new JPopupMenu().getBorder());
          dialog.setSize(component.getPreferredSize());
          dialog.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
              if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                dialog.setVisible(false);
              }
            }
          });
        }
    
        @Override
        public void show() {
          dialog.addWindowFocusListener(this);
          dialog.setVisible(true);
        }
    
        @Override
        public void hide() {
          dialog.setVisible(false);
          dialog.removeWindowFocusListener(this);
        }
    
        public void windowGainedFocus(WindowEvent e) {
          // NO-OP
        }
    
        public void windowLostFocus(WindowEvent e) {
          hide();
        }
      }
    
      public static void main(String[] args) {
        final PopupTester popupTester = new PopupTester();
        popupTester.setLayout(new FlowLayout());
        popupTester.setSize(300, 100);
        popupTester.add(new JButton("Click Me") {
          @Override
          protected void fireActionPerformed(ActionEvent event) {
            Point location = getLocationOnScreen();
            int x = (int) location.getX();
            int y = (int) (location.getY() + getHeight());
            JLabel myComponent = new JLabel("Howdy");
    
            MessagePopup popup = new MessagePopup(popupTester, myComponent, x, y);
            popup.show();
          }
        });
        popupTester.add(new JButton("No Click Me"));
        popupTester.setVisible(true);
        popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      }
    }
    

    我知道这是一个老问题,但我真的需要弹出在我的情况下工作。所以我尝试了一些方法,下面是我的解决方案

    向添加到弹出窗口的组件添加
    FocusListener
    ,并在该组件上编程
    focusLost
    事件,以在焦点丢失时隐藏弹出窗口。显示弹出窗口后,在组件上调用
    requestFocus
    方法

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.FocusAdapter;
    import java.awt.event.FocusEvent;
    
    import javax.swing.*;
    
    public class PopupTester extends JFrame {
        JButton myButton = new JButton("Click Me");
        JLabel myComponent = new JLabel("Howdy");
        Popup popup = null;
    
        public PopupTester() {
            setLayout(new FlowLayout());
            setSize(300, 100);
            add(myButton);
            add(new JButton("No Click Me"));
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            myComponent.addFocusListener(new FocusAdapter() {
                public void focusLost(FocusEvent e) {
                    if (popup != null) {
                        popup.hide();
                    }
                }
            });
    
            myButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    if (popup != null) {
                        popup.hide();
                        popup = null;
                    }
    
                    Point location = myButton.getLocationOnScreen();
                    int y = (int) (location.getY() + myButton.getHeight());
                    int x = (int) location.getX();
                    popup = PopupFactory.getSharedInstance().getPopup(PopupTester.this, myComponent, x, y);
                    popup.show();
                    myComponent.requestFocus();
                }
            });
        }
    
        public static void main(String[] args) {
            PopupTester popupTester = new PopupTester();
            popupTester.setVisible(true);
        }
    }
    

    好建议。对于一个大的应用程序来说,这变得很困难,因为我不能在屏幕上的每个组件上都添加鼠标侦听器。遗憾的是,
    弹出窗口
    本身并不是
    JComponent
    。然后,您可以附加一些侦听器来捕获失去焦点的事件。也许考虑使用<代码> jCalue,然后简单地>代码> MouseListener < /代码>及其<代码> MouthEXIT()/代码>方法.j对话框附带窗口装饰的行李。我能够模拟一些使用JWindow的东西,但是有大量的手动事件处理,结果仍然很奇怪。Popup真的从根本上被破坏了吗?它不能支持这样的东西吗?它扩展了
    对象
    所以。。。但是,向正在传递到
    弹出式
    构造函数中的内容
    组件
    中添加
    鼠标侦听器
    可能会起作用?这是一个很好的解决方案。将WindowFocusListener界面添加到弹出窗口就可以做到这一点。我最终使用了JWindow而不是JDialog,因为我不想要窗口装饰。我将发布最终的解决方案。我也能够让这种方法起作用。结果就简单多了。显然,菜单失去焦点时隐藏的行为只有在将主框架作为show()方法的调用程序传递时才起作用。如果您使用setVisible(true),您将无法获得所需的行为。我也经常这样做。只需将JPOppMenu的布局设置为BorderLayout,并使用中心约束添加内容。JPopupMenu呈现任意swing内容没有问题。我尝试了更多的态度(无边界JDialog、javax.swing.Popup),这似乎是最好的解决方案。