3

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

在此处输入图像描述

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

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

TrayIcon另外,如果任务栏是隐藏的,我添加的时候会不会产生异常SystemTray

4

3 回答 3

8

在 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;

  }
}
于 2013-01-21T01:59:36.237 回答
5

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

看到这个:

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

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

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

从 Java 调用 Win32 API 方法

于 2013-01-21T01:53:28.787 回答
0

我按照 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

于 2017-11-02T21:52:35.960 回答