【问题标题】:How do I determine the position of the system tray on the screen?如何确定系统托盘在屏幕上的位置?
【发布时间】:2013-01-21 01:49:10
【问题描述】:

所以我想在用户单击系统托盘中的图标时显示JPopupMenu。但是,任务栏可以位于屏幕上的任何位置——底部、顶部、右侧、左侧。

如何确定系统托盘的位置以便显示弹出窗口?
getX() 和getY() 可以获得点击的坐标。可以做一些数学运算来正确显示弹出窗口吗?

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

另外,如果任务栏是隐藏的,我在SystemTray中添加TrayIcon会产生异常吗?

【问题讨论】:

    标签: java


    【解决方案1】:

    在 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<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(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;
    
      }
    }
    

    【讨论】:

    • 是的,你的任务栏是我问这个问题的原因。我从上一个关于添加TrayIcon 的问题中观察到,您的位于顶部:) 现在如何正确显示JPopup?
    • 这实际上在我之前对你的问题的回答中得到了证明,但我添加了另一个例子......
    • 我修复了定位代码中的一个错误,它在实际开始确定之前使用弹出窗口的宽度和高度,现在改用首选大小
    【解决方案2】:

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

    看这个:

    How do I get the taskbar's position and size?

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

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

    Calling Win32 API method from Java

    【讨论】:

    • 我认为一点点数学计算可以做到这一点?在mouseClicked(MouseEvent e)方法中,可以使用e.getX()和e.getY()获取点击的x和y坐标
    • @LittleChild 我以为你问的是托盘位置,而不是鼠标点击位置。我不是 Java 程序员,对此我无能为力。对不起。
    【解决方案3】:

    我按照 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-of-notifyicon?forum=winforms

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      • 2021-12-14
      相关资源
      最近更新 更多