如何确定系统托盘在屏幕上的位置?

因此,每当用户单击系统托盘中的图标时,我都希望显示JPopupMenu 。 但是,任务栏可以位于屏幕上的任何位置 – 底部,顶部,右侧,左侧。

在此处输入图像描述

如何确定sys托盘的位置以便显示弹出窗口?
getX()getY()可以获得点击的坐标。 可以进行一些数学计算以正确显示弹出窗口吗?

一个简单的解释和示例代码将不胜感激。

此外,如果任务栏被隐藏,当我将TrayIcon添加到SystemTray时会产生exception吗?

Swing本身没有真正的方法可以做到这一点,但是,您可以通过以下方式获得可能的位置…

 GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); Rectangle bounds = gd.getDefaultConfiguration().getBounds(); Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration()); Rectangle safeBounds = new Rectangle(bounds); safeBounds.x += insets.left; safeBounds.y += insets.top; safeBounds.width -= (insets.left + insets.right); safeBounds.height -= (insets.top + insets.bottom); System.out.println("Bounds = " + bounds); System.out.println("SafeBounds = " + safeBounds); Area area = new Area(bounds); area.subtract(new Area(safeBounds)); System.out.println("Area = " + area.getBounds()); 

哪个输出

 Bounds = java.awt.Rectangle[x=0,y=0,width=2560,height=1600] SafeBounds = java.awt.Rectangle[x=0,y=40,width=2560,height=1560] Area = java.awt.Rectangle[x=0,y=0,width=2560,height=40] 

对于我的系统(注意,我的任务栏位于屏幕顶部)

更新

正如我之前关于托盘图标的问题的回答所示……

 public class TestTaskIcon { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { Image img = null; try { img = ImageIO.read(new File("floppy_disk_red.png")); } catch (IOException e) { e.printStackTrace(); } TrayIcon ti = new TrayIcon(img, "Tooltip"); ti.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { Rectangle bounds = getSafeScreenBounds(e.getPoint()); JPopupMenu popup = new JPopupMenu(); popup.add(new JLabel("hello")); Point point = e.getPoint(); int x = point.x; int y = point.y; if (y < bounds.y) { y = bounds.y; } else if (y > bounds.y + bounds.height) { y = bounds.y + bounds.height; } if (x < bounds.x) { x = bounds.x; } else if (x > bounds.x + bounds.width) { x = bounds.x + bounds.width; } if (x + popup.getPreferredSize().width > bounds.x + bounds.width) { x = (bounds.x + bounds.width) - popup.getPreferredSize().width; } if (y + popup.getPreferredSize().height > bounds.y + bounds.height) { y = (bounds.y + bounds.height) - popup.getPreferredSize().height; } popup.setLocation(x, y); popup.setVisible(true); } }); try { SystemTray.getSystemTray().add(ti); } catch (AWTException ex) { Logger.getLogger(TestTaskIcon.class.getName()).log(Level.SEVERE, null, ex); } } }); } public static Rectangle getSafeScreenBounds(Point pos) { Rectangle bounds = getScreenBoundsAt(pos); Insets insets = getScreenInsetsAt(pos); bounds.x += insets.left; bounds.y += insets.top; bounds.width -= (insets.left + insets.right); bounds.height -= (insets.top + insets.bottom); return bounds; } public static Insets getScreenInsetsAt(Point pos) { GraphicsDevice gd = getGraphicsDeviceAt(pos); Insets insets = null; if (gd != null) { insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration()); } return insets; } public static Rectangle getScreenBoundsAt(Point pos) { GraphicsDevice gd = getGraphicsDeviceAt(pos); Rectangle bounds = null; if (gd != null) { bounds = gd.getDefaultConfiguration().getBounds(); } return bounds; } public static GraphicsDevice getGraphicsDeviceAt(Point pos) { GraphicsDevice device = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice lstGDs[] = ge.getScreenDevices(); ArrayList lstDevices = new ArrayList(lstGDs.length); for (GraphicsDevice gd : lstGDs) { GraphicsConfiguration gc = gd.getDefaultConfiguration(); Rectangle screenBounds = gc.getBounds(); if (screenBounds.contains(pos)) { lstDevices.add(gd); } } if (lstDevices.size() > 0) { device = lstDevices.get(0); } else { device = ge.getDefaultScreenDevice(); } return device; } } 

我认为您无法确定系统托盘的位置,但您可以获得整个任务栏的位置和大小。 你必须使用WINAPI(shell32.dll)。

看到这个:

如何获得任务栏的位置和大小?

这是C#中的示例,但WINAPI在java中可用。

在这里您可以找到有关Java + WINAPI的信息:

从Java调用Win32 API方法

我按照Kamil的方法使用JNA和Win32 DLL获取任务栏大小。 似乎该机制可用于检测任务栏,但它正在挑战以获得精确的图标位置。

JNA代码使用Kamil提供的链接,用Java实现:

 package at.cone.core.tray; import java.awt.Rectangle; import com.sun.jna.Native; import com.sun.jna.platform.win32.Shell32; import com.sun.jna.platform.win32.ShellAPI.APPBARDATA; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.UINT; import com.sun.jna.platform.win32.WinDef.UINT_PTR; public class SystemTray { private static final String TASKBAR_CLASSNAME = "Shell_TrayWnd"; private static UINT_PTR UINT_PTR_ZERO = new UINT_PTR(0); /* * See * https://msdn.microsoft.com/en-us/library/windows/desktop/bb762108(v=vs.85). * aspx */ private static DWORD ABS_ALWAYSONTOP = new DWORD(0x2); private static DWORD ABM_GETSTATE = new DWORD(0x4); private static DWORD ABM_GETTASKBARPOS = new DWORD(0x5); private static DWORD ABM_GETAUTOHIDEBAR = new DWORD(0x7); static { System.setProperty("jna.library.path", YOUR_PATH_TO_JNA_DLLS); } private UINT position; private Rectangle bounds; private boolean alwaysOnTop; private boolean autoHide; public SystemTray() { HWND hTaskbar = User32.INSTANCE.FindWindow(TASKBAR_CLASSNAME, null); APPBARDATA data = new APPBARDATA(); data.cbSize = new DWORD(Native.getNativeSize(APPBARDATA.class));// (uint) Marshal.SizeOf(typeof(APPBARDATA)); data.hWnd = hTaskbar; UINT_PTR result = Shell32.INSTANCE.SHAppBarMessage(ABM_GETTASKBARPOS, data); if (result == UINT_PTR_ZERO) throw new IllegalStateException(); this.position = (UINT) data.uEdge; this.bounds = new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top); data.cbSize = new DWORD(Native.getNativeSize(APPBARDATA.class)); result = Shell32.INSTANCE.SHAppBarMessage(ABM_GETSTATE, data); long state = result.longValue(); this.alwaysOnTop = (state & ABS_ALWAYSONTOP.longValue()) == ABS_ALWAYSONTOP.longValue(); this.autoHide = (state & ABM_GETAUTOHIDEBAR.longValue()) == ABM_GETAUTOHIDEBAR.longValue(); } public Rectangle getBounds() { return bounds; } } 

但是这个实现主要考虑任务栏的位置。 确切的图标位置机制仍然可以改进 – 请参阅https://social.msdn.microsoft.com/Forums/windows/en-US/4ac8d81e-f281-4b32-9407-e663e6c234ae/how-to-get-screen-coordinates-的-NotifyIcon的?论坛=的WinForms