Gestire la System Tray in Java

di  Antonio Coschignano, mercoledì 09 giugno 2010
La System Tray conosciuta anche come Taskbar Status Area su Microsoft Windows oppure Notification Area su ambienti GNOME è quella barra che si trova solitamente in basso a destra nel desktop:

System Try Windows

Con il rilascio della Java SE 6 è stato introdotto il supporto a questa funzionalità definita all'interno delle librearia Java AWT (Abstract Windows Toolkit) quindi implementata per ogni piattaforma che sia Microsoft Windows oppure Linux o Mac Os. Prima era possibile gestire la system tray solo ed esclusivamente utilizzando le JNI (Java Native Interface), quindi in base al sistema operativo bisognava creare delle funzioni in C ed interfaccairli con java.

Java System Tray

La classe che si occupa di gestire questa funzionalità grafica è java.awt.SystemTray. Questa classe implementa innanzitutto due metodi statici, di cui uno si occupa di verificare che la funzionalità sia disponibile per la piattaforma su cui stiamo lavorando isSupported() mentre l'altro metodo getSystemTray() ritorna un riferimento alla system tray della piattaforma:
import java.awt.SystemTray;
....
SystemTray systemTray = null;
if(SystemTray.isSupported()) {
  systemTray = SystemTray.getSystemTray();
}
Il metodo getSystemTray() può generare un'eccezione (UnsupportedOperationException) in caso la funzionalità non è disponibile per un particolare sistema operativo. Quindi prima di invocare il metodo è consigliabile effettuare un controllo tramite isSupported().

Adesso ottenuta un'istanza della classe bisogna aggiungere un'icona e gestirne gli eventi, associandone magari un popupmenu oppure un JFrame etc. La classe che si occupa di gestire queste funzioni è java.awt.TrayIcon a cui è associata un'immagine che rappresenta l'icona che viene visualizzata nella system tray. Quindi per prima cosa dobbiamo caricare un'immagine attraverso il Toolkit di sistema:
String path="....";
Image img = Toolkit.getDefaultToolkit().getImage(path);
Ora bisogna creare un'istanza della TrayIcon passando l'oggetto Image nel costruttore:
TrayIcon trayIcon = new TrayIcon(img);
Aggiungiamo la TrayIcon alla SystemTray tramite il metodo add():
systemTray.add(trayIcon);
Questo che abbiamo fatto semplicemente visualizza nella system tray l'icona che abbiamo caricato attraverso il Toolkit. Per associare altri componenti all'icona adesso bisogna implementare dei listener che in base agli eventi generati visualizza o nasconde gli oggetti grafici da noi definiti oppure effettua altre operazioni come l'uscita dall'applicazione e la rimozione della TrayIcon dalla system tray. Gli eventi vengono generati dal mouse quindi è opportuno implementare un MouseListener o ridefinire un MouseAdapter e registrarlo nella TrayIcon. Ad esempio, immaginiamo di avere un JFrame e ad ogni click del mouse viene visualizzato o nascosto in base allo stato precedente:
JFrame frame = new JFrame("Demo");

private class MyMouseListener exetnds MouseAdapter {
  @Override
  public void mouseClicked(MouseEvent e) {
      if(frame.isVisible())
        frame.setVisible(false);
      else frame.setVisible(true);
  }
}

String path="....";
Image img = Toolkit.getDefaultToolkit().getImage(path);
TrayIcon trayIcon = new TrayIcon(img);
trayIcon.addMouseListener(new MyMouseListener());
if(SystemTray.isSupported()) {
  SystemTray.getSystemTray().add(trayIcon);
}
Un MouseEvent può comunque indicarci quale tasto del mouse è stato premuto (getButton()), destro o sinistro, quindi è possibile associare una funzione per la visualizzazione dell'oggetto grafico oppure la visualizzazione di un JPopupMenu con diverse voci (JMenuItem) dove ciascuno gestisce altri listener dove è possibile associare altri oggetti grafici o altre funzionalità.

Esempio con un JFrame e JPopupMenu

In questo esempio vediamo una semplice applicazione dove ad una TrayIcon è associato un JFrame e un JPopupMenu. In poche parole vengono gestiti gli eventi del mouse. Quando clicchiamo con il tasto destro viene visualizzato un JPopupMenu con le voci visualizza/nascondi/esci. Mentre se usiamo il tasto sinistro viene visualizzato o nascosto il JFrame:
import java.awt.AWTException;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

public class SystemTrayDemo extends JFrame {

  private TrayIcon trayIcon;
  private JPopupMenu popupMenu;

  public SystemTryDemo() throws HeadlessException {
    super("SystemTrayDemo");
    setSize(300, 300);
    create();
  }

  private void create() {
    try {
      if (SystemTray.isSupported()) {
        popupMenu = createPopupMenu();
        SystemTray systemTray = SystemTray.getSystemTray();
        Image img = Toolkit.getDefaultToolkit().getImage("c://icon.gif");
        trayIcon = new TrayIcon(img);
        systemTray.add(trayIcon);
        trayIcon.addMouseListener(new SystemTrayMouseListener());
      }
    } catch (AWTException ex) {}
  }

  private JPopupMenu createPopupMenu() {
    JPopupMenu p = new JPopupMenu();
    JMenuItem apri = new JMenuItem("Apri");
    JMenuItem nascondi = new JMenuItem("Nascondi");
    JMenuItem esci = new JMenuItem("Esci");
    apri.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        setVisible(true);
      }
    });

    nascondi.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        setVisible(false);
      }
    });

    esci.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        setVisible(false);
        SystemTray.getSystemTray().remove(trayIcon);
        System.exit(0);
      }
    });

    p.add(apri);
    p.add(nascondi);
    p.addSeparator();
    p.add(esci);
    return p;
  }

  private class SystemTrayMouseListener extends MouseAdapter {

    @Override
    public void mouseClicked(MouseEvent e) {
      if (e.getButton() == 1) { //Pulsante sinistro del mouse
        if (isVisible()) {
          setVisible(false);
        } else {
          setVisible(true);
        }
      } else if (e.getButton() == 3) {//Pulsante destro del mouse
         popupMenu.show(e.getComponent(), e.getX(), e.getY());
      }
    }
  }

  public static void main(String... argv) throws AWTException {
    SystemTrayDemo demo = new SystemTrayDemo();
  }
}
Questo è uno screenshots dell'applicazione in esecuzione su Windows XP:

Demo System Tray Icon

Come potete notare, per utilizzare questa funzionalità bisogna avere una buona padronanza con i listener e il modello ad eventi per le interfaccie grafiche definito in java. A tal proposito vi consiglio di consultare quest'articolo La gestione degli eventi in java Swing dove ho trattato l'argomento in maniera specifica.
Altri link che potrebbero interessarti
  • » Una lista concatenata in java
  • » Tutorial JFileChooser in Java Swing
  • » La gestione delle Stringhe in java
  • » La gestione degli eventi in java Swing
  • » La classe Collections
  • » Java Generics
  • » Gestire la clipboard in Java Swing
  • » Connessioni HTTP in Java