Как скрыть всплывающее окно Swing при нажатии в другом месте - PullRequest
12 голосов
/ 06 апреля 2010

У меня есть всплывающее окно, которое отображается, когда пользователь нажимает кнопку. Я хотел бы скрыть всплывающее окно, когда происходит любое из следующих событий:

  1. Пользователь щелкает где-то еще в приложении. (Например, фоновая панель)
  2. Пользователь сворачивает приложение.

В JPopupMenu есть такое поведение, но мне нужно больше, чем просто 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);
    }
}

Ответы [ 5 ]

11 голосов
/ 06 апреля 2010

Используйте JPopupMenu. Вы можете добавить к нему любой компонент, а не только пункты меню.

6 голосов
/ 06 апреля 2010

Как отметил Пейтон в предыдущем комментарии, Popup не является JComponent, с которым слушатели могут быть легко связаны. Но, как говорится в его документации, «реализации 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);
    }
}
2 голосов
/ 07 апреля 2010

Спасибо Пейтону и Ноэлю Ангу за то, что они направили меня в правильном направлении! Вот решение, которое я выбрал. Я просто включил это здесь, чтобы другие могли извлечь из этого пользу.

В итоге я выбрал 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);
  }
}
2 голосов
/ 06 апреля 2010

Вы можете добавить MouseListener на фоновую панель и скрыть всплывающее окно, когда кто-то нажимает на панель.

Чтобы реагировать на минимизацию приложения, используйте WindowListener , прикрепленный к JFrame .

и т. Д. И т. Д. Может показаться утомительным, но, безусловно, сработает.

1 голос
/ 07 апреля 2010

Вы можете добавить FocusListener в ваше всплывающее окно и утилизировать его, когда он теряет фокус. Однако это вызовет некоторые проблемы, когда потеря фокуса вызвана каким-либо другим приложением (новые окна выходят на передний план, вы переключаете виртуальные рабочие столы и т. Д.)

Но, возможно, вы (а) знаете, что это не может произойти в вашем случае, или (б) в любом случае захотите закрыть всплывающее окно в любом случае, подход, основанный на фокусе, все еще может быть вам интересен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...