2010-04-05 3 views
12

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

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

ответ

6

Как отметил Пайтон в предыдущем комментарии, 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); 
    } 
} 
+0

Это хорошее решение. Добавление интерфейса WindowFocusListener в Popup делает трюк. В итоге я использовал JWindow вместо JDialog, потому что мне не нравились декорации окон. Я отправлю окончательное решение. –

2

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

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

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

+0

Хорошее предложение. Это становится жестким с большим приложением, потому что я не могу добавить MouseListener для каждого компонента на экране. –

+0

Жаль, что 'Popup' сам по себе не является« JComponent ». Затем вы можете прикрепить некоторый слушатель, чтобы поймать события, когда он теряет фокус. Возможно, рассмотрим использование 'JDialog', а затем просто' MouseListener' и его метод 'mouseExited()'. – pajton

+0

JDialog поставляется с багажом оконных украшений. Мне удалось издеваться над чем-то, что использует JWindow, но есть тонна ручной обработки событий, и это все еще заканчивается довольно изворотливым. Действительно ли Popup принципиально нарушен, что он не может поддерживать что-то подобное? –

10

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

+3

Мне тоже удалось заставить этот подход работать. Это становится намного проще. По-видимому, поведение, которое следует скрывать, когда меню теряет фокус, работает только тогда, когда вы передаете основной кадр в качестве invoker метода show(). Если вы используете setVisible (true), вы не получите желаемого поведения. –

+2

Я также делаю это регулярно. Просто установите макет JPopupMenu в BorderLayout и добавьте свое содержимое с ограничением CENTER. JPopupMenu не имеет проблем с отображением произвольного содержимого swing. – CarlG

+0

Я пробовал больше отношений (без полей JDialog, javax.swing.Popup), и это кажется лучшим решением. –

1

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

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

2

Спасибо 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); 
    } 
} 
Смежные вопросы