2014-11-25 3 views
3

Я работаю над окном предложений с автоматическим раскрывающимся списком (например, Google), используя JWindow (а не JPopupMenu). Мой раскрывающийся список JWindow не является настраиваемым и не модальным (текстовое поле должно сохранять фокус во время ввода пользователем).Как получить уведомление при нажатии/нажатии строки заголовка окна

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

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

Я боюсь, что нет ни одного события для этого, так как я ничего не получаю с этим слушателем:

Toolkit.getDefaultToolkit().addAWTEventListener(myTestListener, Integer.MAX_VALUE); 

Как JPopupMenu добиться такого поведения, так или иначе?

EDIT: добавлен 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); 
    } 
} 
+0

Вы пробовали 'WindowListener.windowActivated()'? – whiskeyspider

+1

Я считаю, что мой прослушиватель событий AWT должен также получить все события окна, включая windowActivated. Но я попробую еще раз ... (после тестирования: нет, я получаю windowActivated события, когда я сворачиваю и восстанавливаю основной фрейм, но не просто щелкнув по строке заголовка окна). – Zalumon

+0

Насколько я помню, это невозможно или, по крайней мере, очень сложно. См. Например, http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6491619 – Radiodef

ответ

0

Если windowActivated() не работает для вас, то в крайнем случае, вы всегда можете установить JFrame, как неукрашенные и создать свою собственную строку заголовка. Он больше не будет выглядеть как родной заголовок, но у вас будет больше контроля, в том числе возможность добавления слушателя мыши.

myFrame.setUndecorated(true); 
+0

спасибо за предложение, но это не вариант для меня. – Zalumon

1

Во-первых, начнем с того, посмотрите на документацию для Toolkit#addAWTEventListener

Параметры:
слушателя - слушателя событий.
eventMask - битовая маска типов событий, чтобы получить

eventMask является битовой Ид мероприятия, которые вы хотите получить. Ладно, это менее впечатляюще, но что это значит, вы должны пройти или зарегистрированный список идентификаторов событий, о которых вы хотели бы получить уведомление ...

Теперь возникает вопрос, какие события нам интересны и как мы получаем идентификаторы ...

Ну AWTEvent содержит список идентификаторов событий, которые работают с Toolkit, AWTEvent.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 ...

+0

Я использовал Integer.MAX_VALUE как ленивый способ прослушать все возможные события. К сожалению, я не получаю никакого уведомления вообще при нажатии на строку заголовка окна. – Zalumon

+0

Список потерянных фокусов всплывающего окна ... – MadProgrammer

+0

Я думал, что слушаю все возможные события, но когда я нажимаю на строку заголовка основного фрейма, я вообще не получаю никакого события. Также теперь я начинаю думать, может быть, я должен был использовать 0xFFFFFFFF вместо Integer.MAX_VALUE. – Zalumon

0

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

Смежные вопросы