2010-04-05 17 views
12

Tengo una ventana emergente que se muestra cuando un usuario hace clic en un botón. Me gustaría ocultar la ventana emergente cuando ocurra cualquiera de los siguientes eventos:¿Cómo se oculta un elemento emergente de Swing cuando se hace clic en otro lugar?

  1. El usuario hace clic en otro lugar de la aplicación. (El panel de fondo, por ejemplo)
  2. El usuario minimiza la aplicación.

El JPopupMenu tiene este comportamiento, pero necesito algo más que JMenuItems. El siguiente bloque de código es una ilustración simplificada para demostrar el uso actual.

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

Respuesta

6

Como señaló pajton en un comentario anterior, Popup no es un JComponent al que los oyentes pueden unirse fácilmente. Pero, como dice su documentación, "las implementaciones de Popup son responsables de crear y mantener sus propios componentes para presentar [su asunto] al usuario".

Por lo tanto, al utilizarlo como su mecanismo de presentación, su ventana emergente tendrá que presentarse como un componente Swing real de todos modos. Haga que se registre en sí en ese componente. Haga que se oculte cuando el componente pierde el foco.

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

Esta es una buena solución. Agregar la interfaz WindowFocusListener al Popup hace el truco. Terminé usando una JWindow en lugar de un JDialog porque no quería las decoraciones de la ventana. Publicaré la solución final. –

2

Puede añadir MouseListener a su panel de fondo y ocultar la ventana emergente cuando alguien hace clic en el panel.

Para reaccionar en la minimización de la aplicación, use WindowListener conectado a JFrame.

Etc, etc. Puede parecer tedioso, pero seguramente funcionará.

+0

Buena sugerencia. Esto se pone difícil con una gran aplicación porque no puedo agregar un MouseListener a cada componente de la pantalla. –

+0

Es una pena que 'Popup' en sí no sea un 'JComponent'. Luego, podría conectar a un oyente para captar eventos cuando pierda el foco. Tal vez considere usar 'JDialog' y luego simplemente' MouseListener' y su método 'mouseExited()'. – pajton

+0

JDialog viene con el equipaje de las decoraciones de las ventanas. Pude burlarme de algo que usa JWindow pero hay una tonelada de manejo manual de eventos y todavía termina siendo bastante peculiar. ¿Realmente Popup está roto de manera fundamental y no admite algo como esto? –

10

Utilice un JPopupMenu. Puede agregarle cualquier componente, no solo los elementos del menú.

+3

Pude hacer que este enfoque también funcionara. Termina siendo mucho más simple. Aparentemente, el comportamiento que se oculta cuando el menú pierde el foco solo funciona cuando pasa el marco principal como el invocador en el método show(). Si usa setVisible (verdadero), no obtendrá el comportamiento deseado. –

+2

También hago esto de forma rutinaria. Simplemente configure el diseño del JPopupMenu en BorderLayout y agregue su contenido con la restricción CENTER. JPopupMenu no tiene problemas para renderizar contenidos arbitrarios. – CarlG

+0

Probé más actitudes (JDialog sin bordes, javax.swing.Popup) y esta parece ser la mejor solución. –

1

Puede agregar un FocusListener a su ventana emergente, y deséchelo cuando pierda el foco. Sin embargo, eso le causará algunos problemas cuando la pérdida de foco se deba a alguna otra aplicación (las nuevas ventanas pasan a primer plano, cambia de escritorios virtuales, etc.)

Pero quizás usted (a) sepa que eso no puede suceder en su caso o (b) querría cerrar la ventana emergente en tales casos de todos modos, un enfoque basado en enfoque puede seguir siendo interesante para usted.

2

Gracias pajton y Noel Ang por hacerme apuntar en la dirección correcta! Aquí está la solución con la que terminé. Solo lo estoy incluyendo aquí para que otros puedan beneficiarse de él.

Terminé yendo con una JWindow ya que no recibe las decoraciones de la ventana, pero sí recibe los eventos de foco.

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