【问题标题】:How to capture trayicon.displayMessage() mouse click on the tooltip baloon如何捕获trayicon.displayMessage() 鼠标单击工具提示气球
【发布时间】:2011-12-10 21:06:03
【问题描述】:

您好,我正在尝试在用户单击消息气球工具提示后显示更多详细信息。

但是,我找不到捕获该事件的方法。

这可能吗?

【问题讨论】:

    标签: java swing events click tray


    【解决方案1】:

    1) 可以通过将 ActionListener 添加到 TrayIcon 来监听 MouseClickEvents,然后消息体监听 MouseClicked

    2) (不直接询问)但如果消息被关闭按钮关闭,并且消息以同样的方式从屏幕上消失,但没有捕获任何事件,我无法给你一个答案

    3) 看起来 Java TrayIcon message close button 应该只是一个解决方案,因为 API 没有实现其他方法,

    import java.awt.*;
    import java.awt.event.*;
    
    public class FullTray {
    
        private static class ShowMessageListener implements ActionListener {
    
            private TrayIcon trayIcon;
            private String title;
            private String message;
            private TrayIcon.MessageType messageType;
    
            ShowMessageListener(TrayIcon trayIcon, String title, String message, TrayIcon.MessageType messageType) {
                this.trayIcon = trayIcon;
                this.title = title;
                this.message = message;
                this.messageType = messageType;
            }
    
            public void actionPerformed(ActionEvent e) {
                trayIcon.addActionListener(new ActionListener() {
    
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("Message Clicked");
                    }
                });
                trayIcon.displayMessage(title, message, messageType);
            }
        }
    
        public static void main(String args[]) {
            Runnable runner = new Runnable() {
    
                public void run() {
                    if (SystemTray.isSupported()) {
                        final SystemTray tray = SystemTray.getSystemTray();
                        Image image = Toolkit.getDefaultToolkit().getImage("gifIcon.gif");
                        PopupMenu popup = new PopupMenu();
                        final TrayIcon trayIcon = new TrayIcon(image, "The Tip Text", popup);
                        MenuItem item = new MenuItem("Error");
                        item.addActionListener(new ShowMessageListener(trayIcon, "Error Title", "Error", TrayIcon.MessageType.ERROR));
                        popup.add(item);
                        item = new MenuItem("Warning");
                        item.addActionListener(new ShowMessageListener(trayIcon, "Warning Title", "Warning", TrayIcon.MessageType.WARNING));
                        popup.add(item);
                        item = new MenuItem("Info");
                        item.addActionListener(new ShowMessageListener(trayIcon, "Info Title", "Info", TrayIcon.MessageType.INFO));
                        popup.add(item);
                        item = new MenuItem("None");
                        item.addActionListener(new ShowMessageListener(trayIcon, "None Title", "None", TrayIcon.MessageType.NONE));
                        popup.add(item);
                        item = new MenuItem("Close");
                        item.addActionListener(new ActionListener() {
    
                            public void actionPerformed(ActionEvent e) {
                                tray.remove(trayIcon);
                            }
                        });
                        popup.add(item);
                        try {
                            tray.add(trayIcon);
                        } catch (AWTException e) {
                            System.err.println("Can't add to tray");
                        }
                    } else {
                        System.err.println("Tray unavailable");
                    }
                }
            };
            EventQueue.invokeLater(runner);
        }
    
        private FullTray() {
        }
    }
    

    【讨论】:

    • 我在第 3 点收到通知。关于...,也许我也在这个论坛上发布这个活动的代码,不确定
    • 某些东西一定已经改变了,因为这个确切的代码在我的配置上不起作用。在 Windows 10 下使用 jdk1.8.0_72 运行。似乎与此 JDK 问题有关 bugs.openjdk.java.net/browse/JDK-8146537
    • 已确认:ActionListener 不适用于 Java 7 - 9(至少在 Windows 10 上) - 请参阅 bugs.openjdk.java.net/browse/JDK-8146537。我已经测试过 Java 11 工作正常
    【解决方案2】:

    尽管这个话题不是最近的话题,但我想我会分享我的解决方案,因为这种事情并没有真正改变。


    如何使用

    要使用此代码,只需创建 1 个公共类和 2 个公共接口。

    现在创建一个 CustomTrayIcon 类的实例,并在完成初始化后调用 addToSystemTray() 方法。

    只要您想显示通知气泡,请调用 showBubbleNotification(...) 方法。

    要删除此托盘图标,只需调用 removeFromSystemTray() 方法即可。


    自定义托盘图标

    这个类是完成所有繁重工作的基础。

    package com.samples;
    
    import java.awt.AWTException;
    import java.awt.Image;
    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.event.MouseEvent;
    import java.awt.event.MouseListener;
    
    /**
     * This class can be used to create an icon in the {@link SystemTray}, providing
     * the current operating system supports the {@link SystemTray}. <br />
     * <br />
     * <b><u>Userful Methods</u>:</b>
     * <ul>
     * <li>
     * Use the {@link CustomTrayIcon#addToSystemTray() addToSystemTray()} method to
     * add this {@link CustomTrayIcon} to the {@link SystemTray}. <br />
     * </li>
     * <li>
     * Use the {@link CustomTrayIcon#removeFromSystemTray() removeFromSystemTray()}
     * method to remove this {@link CustomTrayIcon} from the {@link SystemTray}. <br />
     * </li>
     * <li>
     * Use the
     * {@link CustomTrayIcon#showBubbleNotification(String, String, MessageType, CustomTrayIconNotificationBubbleClickEvent)
     * showBubbleNotification(String, String, MessageType,
     * CustomTrayIconNotificationBubbleClickEvent)} method to show a bubble notification to
     * the user. <br />
     * </li>
     * <li>
     * Use the {@link CustomTrayIcon#setClickDetectionDelay_override(int)
     * setClickDetectionDelay_override(int)} method to override the default delay to
     * be used when detecting if a user performed a single/double/triple click.</li>
     * </ul>
     * 
     * @author Matthew Weiler
     * */
    public abstract class CustomTrayIcon extends TrayIcon implements CustomTrayIconMouseInterface
    {
    
        /* PRIVATE CONSTANTS */
        /**
         * This will be used as the millisecond delay between clicks to detect
         * single or double clicks. <br />
         * <br />
         * <b><u>Default Value</u>:</b> 300
         * */
        private static final int DEFAULT_CLICK_DETECTION_DELAY = 300;
        /**
         * This will be used to ensure that we can synchronize the click counter
         * changes.
         * */
        private static final Object clickLock = new Object();
    
        /* PRIVATE VARIABLES */
        /**
         * This will store the override value to be used in-place of the
         * {@link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
         * default value.
         * 
         * @see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
         * */
        private int clickDetectionDelay_override = -1;
        /**
         * This will store the {@link CustomTrayIconNotificationBubbleClickEvent} which
         * should be executed the next time that an {@link ActionEvent} is fired by
         * the user clicking a notification bubble.
         * */
        private CustomTrayIconNotificationBubbleClickEvent newBubbleAction = null;
        /**
         * This will be used to keep track of which click was responsible for the
         * current click timer.
         * */
        private long clickOwnerIndex = 0L;
        /**
         * This will be used to keep track of how many clicks occurred in the
         * current click detection cycle.
         * */
        private int clickCounter = 0;
    
        /* CONSTRUCTORS */
        /**
         * This will create a new instance of a {@link CustomTrayIcon}.
         * 
         * @param image
         *            The {@link Image} to display as the icon in the notification
         *            tray.
         * */
        public CustomTrayIcon(final Image image)
        {
            super(image);
            this.postCreateTasks();
        }
    
        /**
         * This will create a new instance of a {@link CustomTrayIcon}.
         * 
         * @param image
         *            The {@link Image} to display as the icon in the notification
         *            tray.
         * @param tooltip
         *            The string to be used as tooltip text; if the value is null no
         *            tooltip is shown.
         * */
        public CustomTrayIcon(final Image image, final String tooltip)
        {
            super(image, tooltip);
            this.postCreateTasks();
        }
    
        /**
         * This will create a new {@link CustomTrayIcon}. <br />
         * The {@link CustomTrayIcon}
         * */
        public CustomTrayIcon(final Image image, final String tooltip, final PopupMenu popup)
        {
            super(image, tooltip, popup);
            this.postCreateTasks();
        }
    
        /* PUBLIC METHODS */
        /**
         * This will determine if the current operating system supports
         * {@link SystemTray}.
         * 
         * @see SystemTray#isSupported()
         * 
         * @return <code>true</code> if the current operating system supports
         *         {@link SystemTray}; <code>false</code> otherwise.
         * */
        public static boolean isSystemTraySupported()
        {
            return SystemTray.isSupported();
        }
    
        /**
         * This method will add this {@link CustomTrayIcon} to {@link SystemTray} . <br />
         * <br />
         * <i>this method may return <code>false</code> if the current operating
         * system does not have or support a {@link SystemTray}</i>
         * 
         * @return <code>true</code> if this {@link CustomTrayIcon} was added;
         *         <code>false</code> otherwise.
         * */
        public boolean addToSystemTray()
        {
            // Ensure that the current operating system supports
            // the SystemTray.
            if (CustomTrayIcon.isSystemTraySupported())
            {
                try
                {
                    // Add this CustomTrayIcon to the SystemTray.
                    SystemTray.getSystemTray().add(this);
                    return true;
                }
                catch (AWTException e)
                {
                    // ignore
                }
            }
            return false;
        }
    
        /**
         * This method will remove this {@link CustomTrayIcon} from the
         * {@link SystemTray}.
         * */
        public void removeFromSystemTray()
        {
            SystemTray.getSystemTray().remove(this);
        }
    
        @Override
        public void displayMessage(final String caption, final String text, final MessageType messageType)
        {
            this.newBubbleAction = null;
            super.displayMessage(caption, text, messageType);
        }
    
        /**
         * This method will show a bubble notification near the
         * {@link CustomTrayIcon}.
         * 
         * @see TrayIcon#displayMessage(String, String, MessageType)
         * 
         * @param title
         *            The title to be displayed in the bubble notification.
         * @param message
         *            The message to be displayed in the bubble notification.
         * @param messageType
         *            The type of {@link MessageType} which will denote the style of
         *            the bubble notification.
         * @param actionOnClick
         *            The {@link CustomTrayIconNotificationBubbleClickEvent} which should be
         *            fired when the user clicks the bubble notification.
         * 
         * @throws NullPointerException
         *             If both caption and text are <code>null</code>.
         * */
        public void displayMessage(final String title, final String message, final MessageType messageType, final CustomTrayIconNotificationBubbleClickEvent actionOnClick)
        {
            this.showBubbleNotification(title, message, messageType, actionOnClick);
        }
    
    /**
     * This method will show a bubble notification near the
     * {@link CustomTrayIcon}.
     * 
     * @see TrayIcon#displayMessage(String, String, MessageType)
     * 
     * @param title
     *            The title to be displayed in the bubble notification.
     * @param message
     *            The message to be displayed in the bubble notification.
     * @param messageType
     *            The type of {@link MessageType} which will denote the style of
     *            the bubble notification.
     * 
     * @throws NullPointerException
     *             If both caption and text are <code>null</code>.
     * */
    public void showBubbleNotification(final String title, final String message, final MessageType messageType)
    {
        this.showBubbleNotification(title, message, messageType, null);
    }
    
        /**
         * This method will show a bubble notification near the
         * {@link CustomTrayIcon}.
         * 
         * @see TrayIcon#displayMessage(String, String, MessageType)
         * 
         * @param title
         *            The title to be displayed in the bubble notification.
         * @param message
         *            The message to be displayed in the bubble notification.
         * @param messageType
         *            The type of {@link MessageType} which will denote the style of
         *            the bubble notification.
         * @param actionOnClick
         *            The {@link CustomTrayIconNotificationBubbleClickEvent} which should be
         *            fired when the user clicks the bubble notification.
         * 
         * @throws NullPointerException
         *             If both caption and text are <code>null</code>.
         * */
        public void showBubbleNotification(final String title, final String message, final MessageType messageType, final CustomTrayIconNotificationBubbleClickEvent actionOnClick)
        {
            this.newBubbleAction = actionOnClick;
            super.displayMessage(title, message, messageType);
        }
    
        /* GETTERS & SETTERS */
        /**
         * This will get the override value to be used in-place of the
         * {@link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
         * default value.
         * 
         * @see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
         * 
         * @return The override value to be used in-place of the
         *         {@link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
         *         CLICK_DETECTION_DELAY} default value.
         * */
        public int getClickDetectionDelay_override()
        {
            return this.clickDetectionDelay_override;
        }
    
        /**
         * This will set the override value to be used in-place of the
         * {@link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY CLICK_DETECTION_DELAY}
         * default value.
         * 
         * @see CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
         * 
         * @param clickDetectionDelay_override
         *            The override value to be used in-place of the
         *            {@link CustomTrayIcon#DEFAULT_CLICK_DETECTION_DELAY
         *            CLICK_DETECTION_DELAY} default value.
         * */
        public void setClickDetectionDelay_override(final int clickDetectionDelay_override)
        {
            this.clickDetectionDelay_override = clickDetectionDelay_override;
        }
    
        /* PRIVATE METHODS */
        /**
         * This method will return the delay to have between each click when
         * determining if the user performed a single/double/triple click.
         * 
         * @return The delay to have between each click when determining if the user
         *         performed a single/double/triple click.
         * */
        private int getClickDetectionDelay()
        {
            if (this.clickDetectionDelay_override > 0)
            {
                return this.clickDetectionDelay_override;
            }
            return CustomTrayIcon.DEFAULT_CLICK_DETECTION_DELAY;
        }
    
        /**
         * This method will be executed as the constructors last task. <br />
         * <br />
         * <i>this method will set some attributes on this {@link CustomTrayIcon} and
         * apply the appropriate listeners</i>
         * */
        private void postCreateTasks()
        {
            this.setImageAutoSize(true);
            this.applyCustomListeners();
        }
    
        /**
         * This will apply the various listeners to this {@link CustomTrayIcon}
         * object.
         * */
        private void applyCustomListeners()
        {
            // Add an ActionListener which will fire when the user
            // clicks a bubble notification.
            this.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e)
                {
                    if (CustomTrayIcon.this.newBubbleAction != null)
                    {
                        CustomTrayIcon.this.newBubbleAction.button1_click();
                        CustomTrayIcon.this.newBubbleAction = null;
                    }
                }
            });
            // Add a MouseListener on this CustomTrayIcon which will
            // fire when the user interacts with this CustomTrayIcon
            // using their mouse.
            this.addMouseListener(new MouseListener()
            {
                @Override
                public void mouseReleased(MouseEvent arg0)
                {
                    // NOT SUPPORTED BY TrayIcon
                }
    
                @Override
                public void mousePressed(MouseEvent arg0)
                {
                    // NOT SUPPORTED BY TrayIcon
                }
    
                @Override
                public void mouseExited(MouseEvent arg0)
                {   
    
                }
    
                @Override
                public void mouseEntered(MouseEvent arg0)
                {   
    
                }
    
                @Override
                public void mouseClicked(MouseEvent arg0)
                {
                    // Clear the bubble notification action as any open
                    // bubbles should be popped/closed when this interaction
                    // occurs.
                    CustomTrayIcon.this.newBubbleAction = null;
                    // If this MouseEvent was triggered by mouse button 1
                    // being clicked... then continue.
                    if (arg0.getButton() == MouseEvent.BUTTON1)
                    {
                        long tmp_clickOwnerIndex = 0L;
                        // Ensure that only one thread can modify the click owner index
                        // at any given time.
                        synchronized (CustomTrayIcon.clickLock)
                        {
                            // Increase the click owner index so that only the very
                            // last click can fire an event.
                            CustomTrayIcon.this.clickOwnerIndex++;
                            // Store the current value of the click owner index
                            tmp_clickOwnerIndex = CustomTrayIcon.this.clickOwnerIndex;
                        }
                        // Extract the click owner index, which was obtained in a
                        // thread-safe way, into a final variable so we can access it
                        // below.
                        final long tmp_clickOwnerIndex_final = tmp_clickOwnerIndex;
                        // Launch a new Thread which will sleep for a pre-determined
                        // period of time to catch the delay of a double/triple click.
                        (new Thread()
                        {
                            int tmp_clickCounter = 0;
    
                            public void run()
                            {
                                // Ensure that only one thread can modify the click
                                // counter at any given time.
                                synchronized (CustomTrayIcon.clickLock)
                                {
                                    // Increase the number of active clicks that occurred
                                    // during the current detection cycle.
                                    CustomTrayIcon.this.clickCounter++;
                                    // Extract the total number of active clicks that occurred
                                    // during the current detection cycle up to this point.
                                    tmp_clickCounter = CustomTrayIcon.this.clickCounter;
                                }
                                try
                                {
                                    // Sleep for the pre-determined multiple click detection
                                    // time.
                                    // This ensures that we give the use enough time to
                                    // actually perform multiple clicks.
                                    Thread.sleep(CustomTrayIcon.this.getClickDetectionDelay());
                                }
                                catch (InterruptedException e)
                                {
                                    // ignore
                                }
                                // Ensure that only one thread can modify the click owner index
                                // at any given time.
                                synchronized (CustomTrayIcon.clickLock)
                                {
                                    // If this click was the last click since the appropriate
                                    // delay, then it's clear that this click should be
                                    // processed.
                                    if (CustomTrayIcon.this.clickOwnerIndex == tmp_clickOwnerIndex_final)
                                    {
                                        // If only one click occurred during the allocated delayed
                                        // time, then fire a button 1 click.
                                        if (tmp_clickCounter == 1)
                                        {
                                            CustomTrayIcon.this.button1_click();
                                        }
                                        // If two clicks occurred during the allocated delayed
                                        // time, then fire a button 2 click.
                                        else if (tmp_clickCounter == 2)
                                        {
                                            CustomTrayIcon.this.button1_doubleClick();
                                        }
                                        // If three clicks occurred during the allocated delayed
                                        // time, then fire a button 3 click.
                                        else if (tmp_clickCounter == 3)
                                        {
                                            CustomTrayIcon.this.button1_tripleClick();
                                        }
                                    }
                                    // Decrease the click owner index which will reset the
                                    // click stack.
                                    CustomTrayIcon.this.clickCounter--;
                                }
                            }
                        }).start();
                    }
                    // If this MouseEvent was triggered by mouse button 2
                    // being clicked... then continue.
                    else if (arg0.getButton() == MouseEvent.BUTTON2)
                    {
                        // Ensure that only one thread can modify the click owner index
                        // at any given time.
                        synchronized (CustomTrayIcon.clickLock)
                        {
                            // Increase the click owner index so that only the very
                            // last click can fire an event.
                            CustomTrayIcon.this.clickOwnerIndex++;
                        }
                        CustomTrayIcon.this.button2_click();
                    }
                    // If this MouseEvent was triggered by mouse button 3
                    // being clicked... then continue.
                    else if (arg0.getButton() == MouseEvent.BUTTON3)
                    {
                        // Ensure that only one thread can modify the click owner index
                        // at any given time.
                        synchronized (CustomTrayIcon.clickLock)
                        {
                            // Increase the click owner index so that only the very
                            // last click can fire an event.
                            CustomTrayIcon.this.clickOwnerIndex++;
                        }
                        CustomTrayIcon.this.button3_click();
                    }
                }
            });
        }
    
    }
    

    CustomTrayIconMouseInterface

    该接口用于通过鼠标外部化各种用户交互。

    package com.samples;
    
    import javax.swing.Popup;
    
    /**
     * This interface contains several methods which can be fired when a user
     * interacts with the {@link CustomTrayIcon} using their mouse.
     * 
     * @author Matthew Weiler
     * */
    public interface CustomTrayIconMouseInterface
    {
    
        /**
         * This method will be fired when the user clicks, with mouse button 1, this
         * {@link CustomTrayIcon}.
         * */
        public void button1_click();
    
        /**
         * This method will be fired when the user double-clicks, with mouse button
         * 1, this {@link CustomTrayIcon}.
         * */
        public void button1_doubleClick();
    
        /**
         * This method will be fired when the user triple-clicks, with mouse button
         * 1, this {@link CustomTrayIcon}.
         * */
        public void button1_tripleClick();
    
        /**
         * This method will be fired when the user clicks, with mouse button 2, this
         * {@link CustomTrayIcon}.
         * */
        public void button2_click();
    
        /**
         * This method will be fired when the user clicks, with mouse button 3, this
         * {@link CustomTrayIcon}. <br />
         * <br />
         * <i>if a {@link Popup} menu is assigned to this {@link CustomTrayIcon}, it
         * will still popup regardless of any actions taken by this method</i>
         * */
        public void button3_click();
    
    }
    

    CustomTrayIconNotificationBubbleClickEvent

    该接口用于外部化与通知气泡的交互。

    package com.samples;
    
    /**
     * This interface contains several methods which can be fired when a user
     * interacts with a bubble notification using their mouse.
     * 
     * @author Matthew Weiler
     * */
    public interface CustomTrayIconNotificationBubbleClickEvent
    {
    
        /**
         * This method will be fired when the user clicks, with mouse button 1, this
         * bubble notification.
         * */
        public void button1_click();
    
    }
    

    【讨论】:

    • 这段代码太冗长了......如果你想显示一个点,你应该更简洁。
    • 我不是在证明一个观点,而是在提供一个解决方案。太多人从 SO 中获取代码样本并将其投入生产,而没有添加适当的错误处理或文档。大多数时候,他们不明白代码是如何工作的。这样做不是发帖者的错或责任,但我不喜欢发布半途而废的解决方案。
    猜你喜欢
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多