Не удается скрыть SystemTray JPopupMenu, когда он теряет фокус
Этот вопрос аналогичен этому. То, что у меня есть, это JPopupMenu
, который появляется из значка в системном трее. На данный момент системный трей является единственным проявлением программы. То есть, нет никаких других открытых окон, значок в системном трее-это единственный способ получить доступ к программе. Я использовал JPopupMenu
над AWT PopupMenu
, потому что я хотел, чтобы система выглядела и чувствовала себя примененной к всплывающему меню - когда я использовал только простой PopupMenu
, я не мог получить внешний вид системы, я просто продолжал получать металлический вид свинга. Я использовал этот обходной путь, чтобы получить такое поведение (описано здесь):
systemTrayPopupMenu = buildSystemTrayJPopupMenu();
trayIcon = new TrayIcon(iconImage, "Application Name", null /* Popup Menu */);
trayIcon.addMouseListener (new MouseAdapter () {
@Override
public void mouseReleased (MouseEvent me) {
if (me.isPopupTrigger()) {
systemTrayPopupMenu.setLocation(me.getX(), me.getY());
systemTrayPopupMenu.setInvoker(systemTrayPopupMenu);
systemTrayPopupMenu.setVisible(true);
}
}
};
Когда я щелкаю правой кнопкой мыши на значке в трее, он показывает меню, и, естественно, когда я делаю выбор, меню исчезает. Однако, когда я вызываю меню, а затем нажимаю из него, оно не исчезает. Чтобы он исчез в данный момент, я должен либо сделать выбор, либо выбрать один из пунктов меню, которые являются отключенный.
Я попытался добавить к немуFocusListener
, однако нет никаких указаний на то, что методы focusLost
или focusGained
когда-либо вызывались. Кроме того, я не могу заставить его исчезнуть, когда другой Window
получает фокус, потому что других окон нет. Поскольку это всплывающее меню исходит от TrayIcon
, а не от обычной кнопки, я не могу использовать решение, упомянутое здесь , чтобы обойти FocusListener
, не вызывая focusLost
.
В конечном счете, то, что мне интересно, это либо :
1) Есть есть способ получить внешний вид системы для нормального AWT PopupMenu
?, или
2) есть ли способ заставить JPopupMenu
исчезнуть, когда он теряет фокус?
EDIT: по запросу, вот мой SSCCE
:
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class SwingSystemTray {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run () {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new SwingSystemTray ();
} catch (Exception e) {
System.out.println("Not using the System UI defeats the purpose...");
e.printStackTrace();
}
}
});
}
protected SystemTray systemTray;
protected TrayIcon trayIcon;
protected JPopupMenu systemTrayPopupMenu;
protected Image iconImage;
public SwingSystemTray () throws IOException {
iconImage = getIcon ();
if (SystemTray.isSupported()) {
systemTray = SystemTray.getSystemTray();
systemTrayPopupMenu = buildSystemTrayJPopupMenu();
trayIcon = new TrayIcon(iconImage, "Application Name", null /* Popup Menu */);
trayIcon.addMouseListener (new MouseAdapter () {
@Override
public void mouseReleased (MouseEvent me) {
if (me.isPopupTrigger()) {
systemTrayPopupMenu.setLocation(me.getX(), me.getY());
systemTrayPopupMenu.setInvoker(systemTrayPopupMenu);
systemTrayPopupMenu.setVisible(true);
}
}
});
try {
systemTray.add(trayIcon);
} catch (AWTException e) {
System.out.println("Could not place item at tray. Exiting.");
}
}
}
protected JPopupMenu buildSystemTrayJPopupMenu () {
final JPopupMenu menu = new JPopupMenu ();
final JMenuItem showMenuItem = new JMenuItem("Show");
final JMenuItem hideMenuItem = new JMenuItem("Hide");
final JMenuItem exitMenuItem = new JMenuItem("Exit");
hideMenuItem.setEnabled(false);
ActionListener listener = new ActionListener () {
@Override
public void actionPerformed (ActionEvent ae) {
Object source = ae.getSource();
if (source == showMenuItem) {
System.out.println("Shown");
showMenuItem.setEnabled(false);
hideMenuItem.setEnabled(true);
}
else if (source == hideMenuItem) {
System.out.println("Hidden");
hideMenuItem.setEnabled(false);
showMenuItem.setEnabled(true);
}
else if (source == exitMenuItem) {
System.exit(0);
}
}
};
for (JMenuItem item : new JMenuItem [] {showMenuItem, hideMenuItem, exitMenuItem}) {
if (item == exitMenuItem) menu.addSeparator();
menu.add(item);
item.addActionListener(listener);
}
return menu;
}
protected Image getIcon () throws IOException {
// Build the 16x16 image programmatically, start with BMP Header
byte [] iconData = new byte[822];
System.arraycopy(new byte [] {0x42,0x4d,0x36,0x03, 0,0,0,0, 0,0,0x36,0,
0,0,0x28,0, 0,0,16,0, 0,0,16,0, 0,0,16,0, 24,0,0,0, 0,0,0,3},
0, iconData, 0, 36);
for (int i = 36; i < 822; iconData[i++] = 0);
for (int i = 56; i < 822; i += 3) iconData[i] = -1;
return ImageIO.read(new java.io.ByteArrayInputStream(iconData));
}
}
3 ответа:
Я нашел хак, который, как я чувствую, будет работать просто отлично. Я еще не тестировал его в Windows XP, Но он работает в Windows 7. Это включает в себя добавление "скрытого диалога", который отображает позади всплывающего меню, как если бы всплывающее меню возникло из скрытого диалога в первую очередь. Единственный реальный трюк-это заставить скрытый диалог оставаться за всплывающим меню. По крайней мере, в Windows 7 он отображается за системным Треем, поэтому вы никогда не увидите его в первую очередь. A
WindowFocusListener
можно добавить к это скрытое диалоговое окно, и поэтому, когда вы щелкаете из всплывающего меню, вы также щелкаете из скрытого диалогового окна. Я добавил эту возможность кSSCCE
, который я опубликовал ранее, чтобы проиллюстрировать, как это работает:Это решение дает мне требование №2, которое я искал, которое состоит в том, чтобы заставитьpackage org.test; import java.awt.*; import java.awt.event.*; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.*; public class SwingSystemTray { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run () { try { /* We are going for the Windows Look and Feel here */ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); new SwingSystemTray (); } catch (Exception e) { System.out.println("Not using the System UI defeats the purpose..."); e.printStackTrace(); } } }); } protected SystemTray systemTray; protected TrayIcon trayIcon; protected JPopupMenu systemTrayPopupMenu; protected Image iconImage; /* Added a "hidden dialog" */ protected JDialog hiddenDialog; public SwingSystemTray () throws IOException { iconImage = getIcon (); if (SystemTray.isSupported()) { systemTray = SystemTray.getSystemTray(); systemTrayPopupMenu = buildSystemTrayJPopupMenu(); trayIcon = new TrayIcon(iconImage, "Application Name", null /* Popup Menu */); trayIcon.addMouseListener (new MouseAdapter () { @Override public void mouseReleased (MouseEvent me) { if (me.isPopupTrigger()) { systemTrayPopupMenu.setLocation(me.getX(), me.getY()); /* Place the hidden dialog at the same location */ hiddenDialog.setLocation(me.getX(), me.getY()); /* Now the popup menu's invoker is the hidden dialog */ systemTrayPopupMenu.setInvoker(hiddenDialog); hiddenDialog.setVisible(true); systemTrayPopupMenu.setVisible(true); } } }); trayIcon.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent ae) { System.out.println("actionPerformed"); } }); try { systemTray.add(trayIcon); } catch (AWTException e) { System.out.println("Could not place item at tray. Exiting."); } } /* Initialize the hidden dialog as a headless, titleless dialog window */ hiddenDialog = new JDialog (); hiddenDialog.setSize(10, 10); /* Add the window focus listener to the hidden dialog */ hiddenDialog.addWindowFocusListener(new WindowFocusListener () { @Override public void windowLostFocus (WindowEvent we ) { hiddenDialog.setVisible(false); } @Override public void windowGainedFocus (WindowEvent we) {} }); } protected JPopupMenu buildSystemTrayJPopupMenu () { final JPopupMenu menu = new JPopupMenu (); final JMenuItem showMenuItem = new JMenuItem("Show"); final JMenuItem hideMenuItem = new JMenuItem("Hide"); final JMenuItem exitMenuItem = new JMenuItem("Exit"); hideMenuItem.setEnabled(false); ActionListener listener = new ActionListener () { @Override public void actionPerformed (ActionEvent ae) { /* We want to make sure the hidden dialog goes away after selection */ hiddenDialog.setVisible(false); Object source = ae.getSource(); if (source == showMenuItem) { System.out.println("Shown"); showMenuItem.setEnabled(false); hideMenuItem.setEnabled(true); } else if (source == hideMenuItem) { System.out.println("Hidden"); hideMenuItem.setEnabled(false); showMenuItem.setEnabled(true); } else if (source == exitMenuItem) { System.exit(0); } } }; for (JMenuItem item : new JMenuItem [] {showMenuItem, hideMenuItem, exitMenuItem}) { if (item == exitMenuItem) menu.addSeparator(); menu.add(item); item.addActionListener(listener); } return menu; } protected Image getIcon () throws IOException { // Build the 16x16 image programmatically, start with BMP Header byte [] iconData = new byte[822]; System.arraycopy(new byte [] {0x42,0x4d,0x36,0x03, 0,0,0,0, 0,0,0x36,0, 0,0,0x28,0, 0,0,16,0, 0,0,16,0, 0,0,16,0, 24,0,0,0, 0,0,0,3}, 0, iconData, 0, 36); for (int i = 36; i < 822; iconData[i++] = 0); for (int i = 56; i < 822; i += 3) iconData[i] = -1; return ImageIO.read(new java.io.ByteArrayInputStream(iconData)); } }
JPopupMenu
исчезнуть, когда он теряет фокус на системном трее, используя внешний вид системы Windows.Примечание : я не получил функцию
JPopupMenu
для работы в системе лоток в CentOS / RedHat Linux. Для этого мне придется просто использовать обычный AWTPopupMenu
.
JPopupMenu не может быть отображен сам по себе. То есть его нужно добавить в окно. Попробуйте использовать WindowListener, а затем скрыть всплывающее окно в событии windowDeactivated (). После того, как всплывающее окно будет видно, вы должны быть в состоянии получить окно с помощью:
Window window = SwingUtilities.windowForComonent(systemTrayPopupMenu);
Я только что использовал MouseListener в меню JPopup, который вызывает поток таймера при выходе мыши; если мышь снова входит, Я сбрасываю флаг "mouseStillOnMenu". Установите "нить.sleep() значение, равное тому, как долго вы хотите, чтобы пользователь мог покинуть меню - если вы обычно нажимаете на пункт меню a, вызывается поведение закрытия меню по умолчанию и закрывает меню.
@Override public void mouseEntered(MouseEvent arg0) { mouseStillOnMenu = true; } @Override public void mouseExited(MouseEvent arg0) { mouseStillOnMenu = false; new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); //waits one second before checking if mouse is still on the menu } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (!isMouseStillOnMenu()) { jpopup.setVisible(false); } } }).start(); }