【问题标题】:Adding JPopupMenu to the TrayIcon将 JPopupMenu 添加到 TrayIcon
【发布时间】:2012-09-21 23:06:30
【问题描述】:

我想将JPopupMenu 作为TrayIcon (ie systemTray.add(trayIcon)) 添加到任务栏,但我还没有找到这样做的方法。来自docs TrayIcon 的构造函数看起来像:

public TrayIcon(Image image,
            String tooltip,
            PopupMenu popup)

有什么办法可以做到吗?

【问题讨论】:

  • 欢迎来到我的世界。令人沮丧的是,这尚未实施。尽管如此,您可能会发现 Using JPopupMenu in TrayIcon 感兴趣
  • 使用 MouseListener 并作用于 mouseReleased 是要走的路。您还可以查看this answer,它用于其他目的,但也会在 TrayIcon 上显示 JPopupMenu。
  • @MadProgrammer 链接的文章现在可以在here找到。

标签: java swing system-tray trayicon jpopupmenu


【解决方案1】:

您可以查看Oracle的教程和示例how to use the system tray

【讨论】:

  • 正如文档/教程所述,对 TrayIcon 的支持是有限的,直到今天,JPopupMenu 无法通过构造函数直接添加到 TrayIcon,因此投反对票。
【解决方案2】:

我在 WinXp / Win7 (Win2003/2008) 上看不到构造函数 public TrayIcon(Image image, String tooltip, PopupMenu popup)nor is Java7 的任何问题

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.UIManager;

public class ActiveTray {

    public static void main(String args[]) throws Exception {
        if (SystemTray.isSupported() == false) {
            System.err.println("No system tray available");
            return;
        }
        final SystemTray tray = SystemTray.getSystemTray();
        PropertyChangeListener propListener = new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent evt) {
                TrayIcon oldTray[] = (TrayIcon[]) evt.getOldValue();
                TrayIcon newTray[] = (TrayIcon[]) evt.getNewValue();
                System.out.println(oldTray.length + " / " + newTray.length);
            }
        };
        tray.addPropertyChangeListener("trayIcons", propListener);
        Icon icon = new BevelArrowIcon(BevelArrowIcon.UP, false, false);
        Image image = iconToImage(icon);
        PopupMenu popup = new PopupMenu();
        MenuItem item = new MenuItem("Hello, World");
        final TrayIcon trayIcon = new TrayIcon(image, "Tip Text", popup);
        ActionListener menuActionListener = new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                trayIcon.displayMessage("Good-bye", "Cruel World", 
                        TrayIcon.MessageType.WARNING);
            }
        };
        item.addActionListener(menuActionListener);
        popup.add(item);
        ActionListener actionListener = new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                tray.remove(trayIcon);
            }
        };
        trayIcon.addActionListener(actionListener);
        tray.add(trayIcon);
    }

    static Image iconToImage(Icon icon) {
        if (icon instanceof ImageIcon) {
            return ((ImageIcon) icon).getImage();
        } else {
            int w = icon.getIconWidth();
            int h = icon.getIconHeight();
            GraphicsEnvironment ge =
                    GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice gd = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gd.getDefaultConfiguration();
            BufferedImage image = gc.createCompatibleImage(w, h);
            Graphics2D g = image.createGraphics();
            icon.paintIcon(null, g, 0, 0);
            g.dispose();
            return image;
        }
    }

    static class BevelArrowIcon implements Icon {

        public static final int UP = 0;         // direction
        public static final int DOWN = 1;
        private static final int DEFAULT_SIZE = 16;
        private Color edge1;
        private Color edge2;
        private Color fill;
        private int size;
        private int direction;

        public BevelArrowIcon(int direction, boolean isRaisedView, 
                boolean isPressedView) {
            if (isRaisedView) {
                if (isPressedView) {
                    init(UIManager.getColor("controlLtHighlight"), 
                            UIManager.getColor("controlDkShadow"), 
                            UIManager.getColor("controlShadow"), 
                            DEFAULT_SIZE, direction);
                } else {
                    init(UIManager.getColor("controlHighlight"), 
                            UIManager.getColor("controlShadow"), 
                            UIManager.getColor("control"), 
                            DEFAULT_SIZE, direction);
                }
            } else {
                if (isPressedView) {
                    init(UIManager.getColor("controlDkShadow"), 
                            UIManager.getColor("controlLtHighlight"), 
                            UIManager.getColor("controlShadow"), 
                            DEFAULT_SIZE, direction);
                } else {
                    init(UIManager.getColor("controlShadow"), 
                            UIManager.getColor("controlHighlight"), 
                            UIManager.getColor("control"), 
                            DEFAULT_SIZE, direction);
                }
            }
        }

        public BevelArrowIcon(Color edge1, Color edge2, Color fill, 
                int size, int direction) {
            init(edge1, edge2, fill, size, direction);
        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            switch (direction) {
                case DOWN:
                    drawDownArrow(g, x, y);
                    break;
                case UP:
                    drawUpArrow(g, x, y);
                    break;
            }
        }

        @Override
        public int getIconWidth() {
            return size;
        }

        @Override
        public int getIconHeight() {
            return size;
        }

        private void init(Color edge1, Color edge2, Color fill, 
                int size, int direction) {
            edge1 = Color.red;
            edge2 = Color.blue;
            this.edge1 = edge1;
            this.edge2 = edge2;
            this.fill = fill;
            this.size = size;
            this.direction = direction;
        }

        private void drawDownArrow(Graphics g, int xo, int yo) {
            g.setColor(edge1);
            g.drawLine(xo, yo, xo + size - 1, yo);
            g.drawLine(xo, yo + 1, xo + size - 3, yo + 1);
            g.setColor(edge2);
            g.drawLine(xo + size - 2, yo + 1, xo + size - 1, yo + 1);
            int x = xo + 1;
            int y = yo + 2;
            int dx = size - 6;
            while (y + 1 < yo + size) {
                g.setColor(edge1);
                g.drawLine(x, y, x + 1, y);
                g.drawLine(x, y + 1, x + 1, y + 1);
                if (0 < dx) {
                    g.setColor(fill);
                    g.drawLine(x + 2, y, x + 1 + dx, y);
                    g.drawLine(x + 2, y + 1, x + 1 + dx, y + 1);
                }
                g.setColor(edge2);
                g.drawLine(x + dx + 2, y, x + dx + 3, y);
                g.drawLine(x + dx + 2, y + 1, x + dx + 3, y + 1);
                x += 1;
                y += 2;
                dx -= 2;
            }
            g.setColor(edge1);
            g.drawLine(xo + (size / 2), yo + size - 1, xo 
                    + (size / 2), yo + size - 1);
        }

        private void drawUpArrow(Graphics g, int xo, int yo) {
            g.setColor(edge1);
            int x = xo + (size / 2);
            g.drawLine(x, yo, x, yo);
            x--;
            int y = yo + 1;
            int dx = 0;
            while (y + 3 < yo + size) {
                g.setColor(edge1);
                g.drawLine(x, y, x + 1, y);
                g.drawLine(x, y + 1, x + 1, y + 1);
                if (0 < dx) {
                    g.setColor(fill);
                    g.drawLine(x + 2, y, x + 1 + dx, y);
                    g.drawLine(x + 2, y + 1, x + 1 + dx, y + 1);
                }
                g.setColor(edge2);
                g.drawLine(x + dx + 2, y, x + dx + 3, y);
                g.drawLine(x + dx + 2, y + 1, x + dx + 3, y + 1);
                x -= 1;
                y += 2;
                dx += 2;
            }
            g.setColor(edge1);
            g.drawLine(xo, yo + size - 3, xo + 1, yo + size - 3);
            g.setColor(edge2);
            g.drawLine(xo + 2, yo + size - 2, xo + size - 1, yo + size - 2);
            g.drawLine(xo, yo + size - 1, xo + size, yo + size - 1);
        }
    }
}

【讨论】:

  • PopupMenu 不是 JPopupMenu,因此不能执行 Actions 之类的操作。
【解决方案3】:

对于当前的TrayIcon 实现,无法直接将JPopupMenuJMenu 添加到TrayIcon。但是,有些人确实设法通过实现自定义MouseListener 来解决它,该MouseListener 仅在Trayicon 处侦听右键单击。当鼠标右键单击时,将弹出菜单的位置设置为event.getXOnScreen()event.getYOnScreen() 并设置为可见。

【讨论】:

    【解决方案4】:

    这是一个已知问题。有一个bug report,其中包含解决方法的概要。我已经在下面进行了调整:

    // Build your popup menu
    final JPopupMenu trayPopup = new JPopupMenu();
    // I'm using actions, there are other ways of doing this.
    trayPopup.add(someFantaticAction);
    
    // Get your tray icon
    trayIcon = new TrayIcon(icon, "My awesome application");
    
    trayIcon.addMouseListener(new MouseAdapter() {
    
        @Override
        public void mouseReleased(MouseEvent e) {
            maybeShowPopup(e);
        }
    
        @Override
        public void mousePressed(MouseEvent e) {
            maybeShowPopup(e);
        }
    
        private void maybeShowPopup(MouseEvent e) {
            if (e.isPopupTrigger()) {
                trayPopup.setLocation(e.getX(), e.getY());
                trayPopup.setInvoker(trayPopup);
                trayPopup.setVisible(true);
            }
        }
    });
    

    【讨论】:

    • trayIcon.addMouseListener(new MouseAdapter() { 无法在 Java 1.8.111 上编译。需要改成MouseListener()
    • 这很奇怪;为什么 Java 8 会拒绝 MouseAdapter?
    • 无论如何,这个解决方案似乎有一个问题:当您在弹出窗口之外单击时没有关闭它。
    【解决方案5】:

    好激动,找到了解决办法。思路如下:

    1. TrayIcon trayIcon = new TrayIcon(image, "Tip Text"); // 不再需要弹出参数。

    2. trayIcon.addMouseListener(new CustomTrayIconMouseListener());

    3. 类 CustomTrayIconMouseListener 扩展了 MouseAdapter,并且在 TrayIconMouseListener 类中你需要完成两件事:

      3.1 定义JPopupMenu类型的成员变量,做你想做的,比如添加一些JMenuItem等等。

      3.2 覆盖mouseClicked方法,然后通过判断鼠标右键点击事件弹出JPopupMenu(setVisible方法)。在弹出JPopupMenu(setLocation方法)之前需要设置它的位置。

    因为我今天也遇到了这个问题,刚才就想到了这个想法,现在正在努力解决,现在正在写代码。也许我会遇到其他问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多