如何捕获trayicon.displayMessage()鼠标点击工具提示气球

时间:2023-01-28 22:30:42

Hi, I am trying to show more detailed information after a user clicks on the message balloon tooltip.

嗨,我想在用户点击消息气球工具提示后显示更详细的信息。

However, I can't find how to capture that event.

但是,我找不到如何捕获该事件。

Is this possible to do?

这可能吗?

2 个解决方案

#1


11  

1) Is possible to listening MouseClickEvents by add ActionListener to the TrayIcon, then Message body listening for MouseClicked

1)可以通过将ActionListener添加到TrayIcon来监听MouseClickEvents,然后Message body侦听MouseClicked

2) (not asked directly) but I can't to give you an answer listening if message was closed by close button, and Message gone away from screen same way, but without catch any event(s)

2)(没有直接询问)但我不能给你一个答案,如果消息是通过关闭按钮关闭,消息以同样的方式离开屏幕,但没有捕获任何事件

3) looks like as this Java TrayIcon message close button should be only one solutions, because API doesn't implements another methods,

3)看起来像这个Java TrayIcon消息关闭按钮应该只是一个解决方案,因为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() {
    }
}

#2


1  

Even though this topic isn't exactly recent, I figured I'd share my solution since this kind of thing doesn't really change.

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


How To Use

如何使用

To use this code, just create the 1 public class and the 2 public interfaces.

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

Now create an instance of the CustomTrayIcon class and call the addToSystemTray() method, once you're done initializing it.

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

Anytime you want to show a notification bubble, call the showBubbleNotification(...) method.

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

To remove this tray icon, just call the removeFromSystemTray() method.

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


CustomTrayIcon

CustomTrayIcon

This class is the base which does all of the heavy lifting.

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

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

CustomTrayIconMouseInterface

This interface is used to externalize various user interactions via the mouse.

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

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

CustomTrayIconNotificationBubbleClickEvent

This interface is used to externalize the interaction with the notification bubble.

此界面用于外部化与通知气泡的交互。

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();

}

#1


11  

1) Is possible to listening MouseClickEvents by add ActionListener to the TrayIcon, then Message body listening for MouseClicked

1)可以通过将ActionListener添加到TrayIcon来监听MouseClickEvents,然后Message body侦听MouseClicked

2) (not asked directly) but I can't to give you an answer listening if message was closed by close button, and Message gone away from screen same way, but without catch any event(s)

2)(没有直接询问)但我不能给你一个答案,如果消息是通过关闭按钮关闭,消息以同样的方式离开屏幕,但没有捕获任何事件

3) looks like as this Java TrayIcon message close button should be only one solutions, because API doesn't implements another methods,

3)看起来像这个Java TrayIcon消息关闭按钮应该只是一个解决方案,因为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() {
    }
}

#2


1  

Even though this topic isn't exactly recent, I figured I'd share my solution since this kind of thing doesn't really change.

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


How To Use

如何使用

To use this code, just create the 1 public class and the 2 public interfaces.

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

Now create an instance of the CustomTrayIcon class and call the addToSystemTray() method, once you're done initializing it.

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

Anytime you want to show a notification bubble, call the showBubbleNotification(...) method.

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

To remove this tray icon, just call the removeFromSystemTray() method.

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


CustomTrayIcon

CustomTrayIcon

This class is the base which does all of the heavy lifting.

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

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

CustomTrayIconMouseInterface

This interface is used to externalize various user interactions via the mouse.

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

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

CustomTrayIconNotificationBubbleClickEvent

This interface is used to externalize the interaction with the notification bubble.

此界面用于外部化与通知气泡的交互。

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();

}