Usando el System Tray desde Java
En esta ocasión vamos a aprender usar el System Tray desde una aplicación hecha en Java. El System Tray o área de notificaciones, es el espacio en el escritorio en donde ‘residen’ aplicaciones que el sistema está ejecutando, pero que no tienen en foco de lo que estamos haciendo.
Antes se usaban librerías nativas + JNI para lograr esto, pero desde hace un tiempo se incluyeron en el API de Java 6 las clases SystemTray y TrayIcon (dentro del paquete java.awt) lo cual nos facilita mucho las cosas.

0. Entendiendo las clases SystemTray y TrayIcon + Prerrequisitos
Antes que nada, debes tener instalada una versión reciente del JDK6. La clase java.awt.SystemTray representa el System Tray del escritorio. Para manipular dicha clase es necesario obtener un objeto estático de esta usando el método SystemTray.getSystemTray() (es decir, no es posible crear instancias de dicha clase). Además, es necesario verificar que el uso de esta clase sea soportada en el sistema donde se ejecuta. Esto lo hacemos usando el método estático SystemTray.isSupported(). Si no hay soporte, este método retornará false. Si haces un llamado al método getSystemTray y tu sistema no está soportado, se lanzará la excepción java.lang.UnsupportedOperationException.
La instancia de SystemTray contiene uno o más TrayIcons, los cuales son añadidos usando el método add(java.awt.TrayIcon), y son eliminados con el método remove(java.awt.TrayIcon).
1. Ejemplo de uso
import java.awt.*;
import java.awt.event.*;
public class EjemploSystemTray {
public EjemploSystemTray() {
//se declara el objeto tipo icono
final TrayIcon iconoSystemTray;
//se verifica que el SystemTray sea soportado
if (SystemTray.isSupported()) {
//se obtiene una instancia estática de la clase SystemTray
SystemTray tray = SystemTray.getSystemTray();
//esta es la imagen de icono
Image imagenIcono = Toolkit.getDefaultToolkit().getImage("icono.png");
//este listener nos permite capturar cualquier tipo de evento
//que se haga con el mouse sobre el icono
MouseListener mouseListener = new MouseListener() {
public void mouseClicked(MouseEvent e) {
System.out.println("Icono del System Tray - Mouse clicked!");
}
public void mouseEntered(MouseEvent e) {
System.out.println("Icono del System Tray - Mouse entered!");
}
public void mouseExited(MouseEvent e) {
System.out.println("Icono del System Tray - Mouse exited!");
}
public void mousePressed(MouseEvent e) {
System.out.println("Icono del System Tray - Mouse pressed!");
}
public void mouseReleased(MouseEvent e) {
System.out.println("Icono del System Tray - Mouse released!");
}
};
//este listener se asociara con un item del menu contextual
//que aparece al hacer click derecho sobre el icono
ActionListener escuchadorSalir = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Saliendo...");
System.exit(0);
}
};
//menu que aparece al hacer click derecho
PopupMenu popup = new PopupMenu();
MenuItem item = new MenuItem("Exit");
item.addActionListener(escuchadorSalir);
popup.add(item);
//iniciamos el objeto TrayIcon
iconoSystemTray = new TrayIcon(imagenIcono, "Prueba System Tray", popup);
//este tipo de listener captura el doble click sobre el icono
ActionListener accionMostrarMensaje = new ActionListener() {
public void actionPerformed(ActionEvent e) {
iconoSystemTray.displayMessage("Titulo mensaje",
"Este es el contenido del mensaje!",
TrayIcon.MessageType.INFO);
}
};
iconoSystemTray.setImageAutoSize(true);
iconoSystemTray.addActionListener(accionMostrarMensaje);
iconoSystemTray.addMouseListener(mouseListener);
//se debe capturar una excepción en caso que falle la adicion de un icono
try {
tray.add(iconoSystemTray);
} catch (AWTException e) {
System.err.println("No es posible agregar el icono al System Tray");
}
}
else
System.err.println("Tu sistema no soporta el System Tray");
}
public static void main(String[] args) {
new EjemploSystemTray();
}
}
Así se ve en Linux (usando Gnome) y en Windows7:

Del ejemplo anterior veamos las líneas más importantes:
- Línea 8: verificamos que tengamos soporte.
- Línea 10: obtenemos una instancia de la clase SystemTray.
- Línea 46: creamos el icono que vamos a añadir.
- Línea 50: invocación del método displayMessage para mostrar globos de notificación.
- Línea 62: añadimos el icono al systray.
2. ¿Cómo usar Swing en vez de AWT?
Si te fijaste bien, el SystemTray es una clase que pertenece al paquete AWT… por alguna razón no se tuvo en cuenta Swing en todo esto. Es por ello que no podemos usar directamente JPopupMenu en vez de PopupMenu. Además, JPopupMenu tampoco es una subclase de PopupMenu por lo cual no podemos usar polimorfismo
Afortunadamente hay algunos trucos para hacerlo funcionar con Swing que nos proporciona varias ventajas: la apariencia del menú sería igual en cualquier sistema operativo, podemos añadir iconos a los items, etc.
El truco más sencillo del cual tengo conocimiento lo leí en el blog de Artem Ananiev, en un post llamado Using JPopupMenu in TrayIcon. He preparado entonces otro ejemplo sencillo en donde se muestra cómo lograr esto:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EjemploSystemTraySwing {
public EjemploSystemTraySwing() {
//se declara el objeto tipo icono
final TrayIcon iconoSystemTray;
//se verifica que el SystemTray sea soportado
if (SystemTray.isSupported()) {
//se obtiene una instancia estática de la clase SystemTray
SystemTray tray = SystemTray.getSystemTray();
//esta es la imagen de icono
Image imagenIcono = Toolkit.getDefaultToolkit().getImage("icono.png");
//este listener se asociara con un item del menu contextual
//que aparece al hacer click derecho sobre el icono
ActionListener escuchadorSalir = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Saliendo...");
System.exit(0);
}
};
//menu que aparece al hacer click derecho
final JPopupMenu popup = new JPopupMenu();
//aniadir un menu con icono (swing)
JMenuItem carita = new JMenuItem("casidiablo.net", new ImageIcon("carita.png"));
popup.add(carita);
popup.addSeparator();
//aniadir el item de salir
JMenuItem item = new JMenuItem("Exit", new ImageIcon("salir.png"));
item.addActionListener(escuchadorSalir);
popup.add(item);
//iniciamos el objeto TrayIcon
iconoSystemTray = new TrayIcon(imagenIcono, "Prueba System Tray", null);
iconoSystemTray.setImageAutoSize(true);
//iconoSystemTray.addMouseListener(mouseListener);
iconoSystemTray.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.setLocation(e.getX(), e.getY()-50);
popup.setInvoker(popup);
popup.setVisible(true);
}
}
});
//se debe capturar una excepción en caso que falle la adicion de un icono
try {
tray.add(iconoSystemTray);
} catch (AWTException e) {
System.err.println("No es posible agregar el icono al System Tray");
}
}
else
System.err.println("Tu sistema no soporta el System Tray");
}
public static void main(String[] args) {
new EjemploSystemTraySwing();
}
}
El resultado es algo como lo que aparece en la primera imagen que acompaña esta entrada. Importante tener en cuenta las líneas resaltadas (40 a 44) que es donde se hace la magia
Como siempre, puedes descargar el código y probar tú mismo:
11 Comentarios | deja el tuyo





RT: @casidiablo: Usando el System Tray desde Java http://casidiablo.in/c8vs22
Muy bueno, gracias pero en el ejemplo de Swing no me aparecía el menú. Logre que se mostrara quitando el:
if (e.isPopupTrigger())
y listo, ya me apareció.
Genial!
Muchas gracias por el aporte, germbow.
Un saludo!
No, eso no está bien, el problema que tenés es este:
isPopupTrigger
public boolean isPopupTrigger()
Returns whether or not this mouse event is the popup menu trigger event for the platform.
Note: Popup menus are triggered differently on different systems. Therefore, isPopupTrigger should be checked in both mousePressed and mouseReleased for proper cross-platform functionality.
Returns:
boolean, true if this event is the popup menu trigger for this platform
Es decir, depende en que plataforma estás. Implementá ambos métodos, y listo, así funciona. =)
Con respecto a lo otro, setInvoker() me está tirando una excepción, que si bien todo sigue funcionando bien, habría que analizar porque tira esa excepción.
Saludos, y gracias.
Ok gracias, siempre es bueno saber el por que de las cosas…
Hey!
muchas gracias
por este post
esta muy interesante para agregarle a nuestros primeros programas
se agradece mucho
sigue así!
casidiablo, me parece genial, precisamente hace 2 minutos estaba pensado en hacer algo de ese estilo, que bien!
justo lo que estaba buscando. Una cosa para lo que usan netbeans. Si quieren que se vean las imagenes deben agreagar esto:
en vez de “icono.png” —-> this.getClass().getResource(“icono.png”));
Existe un problema con la versión de swing.
Cuando abres el popup, si pinchas fuera en cualquier sitio, este debería cerrarse, al igual que el de AWT y en lugar de eso permanece visible todo el tiempo. (Parte queda oculta tras la barra de tareas)
He intentado solucionarlo de varios modos, pero no he encontrado ninguna solución demasiado buena.
Se os ocurre algo???
Gracias.
mira, yo encontre esta solucion capaz te sea util
http://www.codr.cc/85d8b5
ese oyente se lo agregas al JPopupMenu tuyo, entonces lo que haces es ver donde esta el mouse y si a la hora de llamar al evento mouseExited, el mouse se encuentra fuera del popup este se hace invisible
saludos
te dejo el codigo completo de mi aplicacion en esta web, espero que la puedas ver
http://www.codr.cc/4e977e