此问题与this one类似。我所拥有的是从系统托盘上的图标弹出的JPopupMenu
。此时,系统托盘是程序的唯一表现形式。也就是说,没有其他窗口打开,系统托盘中的图标是我可以访问该程序的唯一方法。我在AWT JPopupMenu
上使用PopupMenu
,因为我想将系统外观应用到弹出菜单中 - 当我只使用普通PopupMenu
时,我无法获得系统的外观和感觉,我只是不断获得Swing的金属外观和感觉。我使用这种方法来解决这种问题(描述here):
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
而非典型按钮,因此我无法使用here提及的解决方案来绕过FocusListener
而不是调用focusLost
。
最终,我想知道的是 :
1)有没有办法让系统的外观和感觉为正常的AWT PopupMenu
?或者
2)有没有办法让JPopupMenu
在失去焦点时消失?
编辑:根据请求,这是我的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));
}
}
答案 0 :(得分:3)
我发现了一个我认为可以很好地工作的黑客。我还没有在Windows XP中测试它,但它可以在Windows 7中运行。这涉及添加一个“隐藏的对话框”,在弹出菜单后面显示,就像弹出菜单来自隐藏的对话框一样第一名。唯一真正的诀窍是让隐藏的对话框留在弹出菜单后面。至少在Windows 7中,它显示在系统托盘后面,所以你从来没有真正看到它。可以将WindowFocusListener
添加到此隐藏对话框中,因此当您单击弹出菜单时,您也会单击隐藏的对话框。我已将此功能添加到我之前发布的SSCCE
中,以说明如何添加此功能:
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));
}
}
此解决方案为我提供了我正在寻找的第2项要求,即当JPopupMenu
使用Windows系统外观失去对系统托盘的关注时,JPopupMenu
会消失。
注意:我没有使用PopupMenu
功能在CentOS / RedHat Linux中的系统托盘上工作。对于那些人,我将只需使用普通的AWT {{1}}。
答案 1 :(得分:0)
JPopupMenu无法单独显示。那就是需要将它添加到窗口中。尝试使用WindowListener,然后在windowDeactivated()事件中隐藏弹出窗口。弹出窗口可见后,您应该可以使用以下命令获取窗口:
Window window = SwingUtilities.windowForComonent(systemTrayPopupMenu);
答案 2 :(得分:-1)
我刚刚在JPopup菜单上使用了MouseListener,它在鼠标退出时调用了一个计时器Thread;如果鼠标重新进入,我重置&#34; mouseStillOnMenu&#34;旗。将&#34; Thread.sleep()值设置为您希望用户离开菜单的时间长 - 如果您正常单击菜单项,则会调用默认菜单关闭行为并关闭菜单。
@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();
}