quarta-feira, 16 de fevereiro de 2011

Swing: Ícone na area de notificação


Aviso: Este post é quase um livro!
Algum tempo atraz, mas precisamente na matéria sobre WindowListener, comentei sobre a lógica necessária para enviar uma Janela Swing para a bandeja do sistema, ou Area de Notificação, ou SystemTray, ou como você gosta de chamar aquele cantinho inferior direito, ao lado do relógio, onde fica um aglomerado de ícones que as vezes nem conhecemos.
Teoricamente tudo é facil, o difícil é apenas implementar a solução ao nosso projeto já finalizado…
Mas vamos parar no pensamento que “tudo é facil”, e seguir em frente com este tutorial que ficou bem grande, e é quase todo código!
Primeiro vamos à lógica necessária:
Se você deseja enviar uma janela para a bandeja do sistema, desista, você nunca vai conseguir isso (ao menos em java), missão impossível.
O que podemos fazer é uma pequena “gambiarra”, invisível aos olhos de meros mortais.
Essa “POG” consiste em:
 – Capturar o evento “MINIMIZAR” ou “FECHAR” ou qualquer outra porcaria que você queira.
 – Deixar a janela que foi minimizada INVISÍVEL!
 – Instanciar uma nova classe que coloca um icone bonitinho ao lado do relógio.
 – Colocar um menu flutuante nesse icone (Menu Popup)
 – Capturar os eventos de click nos itens deste menu
6º - Caso o item selecionado seja “Abrir novamente a jenela principal” deve-se deixar o frame principal VISIVELnovamente e “matar” as variáveis utilizadas para criar o icone bonitinho ao lado do relógio.

Viu que legal? Não se envia uma janela para o relógio, mas engana-se o usuário deixando a janela invisível e criando OUTRA janela sem corpo, apenas com um icone e um menu Popup!
Agora que já estamos com a faca e o queijo na mão, vamos ao que interessa!
Antes de mais nada, precisamos de uma janela, para que esta seja minimizada, então vamos criar uma classe chamada “Janelinha“, conforme exemplo:
package br.teste.area.notificacao;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.WindowConstants;
public class Janelinha extends javax.swing.JFrame {

 static AreaNotificacao an null;
 static Janelinha j = null;
 public static void main(String[] args) {
      j = new Janelinha();
      j.setVisible(true);
}

 public Janelinha() {
      super(“Oi, me minimiza…”);
      constroiJanelinha();
}

 private void constroiJanelinha() {
  try {
   setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
   setLocationRelativeTo(null);
   pack();
   setSize(20050);
 
   //Listener que capitura o evento “minimizar”   addWindowListener(
     new WindowAdapter(){
      public void windowIconified(WindowEvent evnt){
       //Deixa a janela atual (Janelinha) invisível!       setVisible(false);
       //Instancia a classe responsável pelo ícone na área de notificação.       getAreaNotificacao();
     
      }
     }
   );
 
  } catch (Exception e) {
   e.printStackTrace();
  }
}
void getAreaNotificacao(){
  if(an == null){
   an = new AreaNotificacao();
  }
}
}
Esta é a classe principal que na verdade é nossa coadjuvante, só servirá para ser minimizada.
Repare no WindowListener, o que ele faz…
No WindowListener, estamos deixando o frame invisível “setVisible(false)”, na verdade ele continua vivinho da silva, a memória ocupada, o processamento em alta, execução de processos à todo vapor, porém não está visivel.
Após esta poção da invisibilidade, está sendo chamado o método interno “getAreaNotificacao()”, e sua única finalidade é instanciar a classe “AreaNotificacao”, e é nesta classe que a mágica vai acontecer!
IMPORTANTE: Repare também que as duas principais variáveis desta classe são static! Mas por que isso? A resposta na prática está logo abaixo, a resposta na teoria é simples, essas duas variáveis serão únicas em toda a aplicação, e precisam ser acessadas por outra janela (a classe AreaNotificacao), quando a classe AreaNotificacao for acessar estes objetos, precisamos ter certeza que eles são os mesmos objetos/variáveis/referências utilizados pela classe Janelinha, caso contrário a mágica não será tão boa.
Agora, vamos à classe secundária que na verdade é nossa protagonista.
package br.teste.area.notificacao;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class AreaNotificacao {
 //Criação da icone, instancia da classe java java.awt.TrayIcon static TrayIcon trayIcon null;

 //Referencia à instancia “J” da classe Janelinha, para acesso da bandeja. static Janelinha pai = Janelinha.j;

 //Construtor, que será chamado quando a classe for instanciada
AreaNotificacao() {
  super();
  criaAreaNotificacao();
}

 static void criaAreaNotificacao(){

  //Verifica se não é possível trabalhar com “TrayIcon”  if (!SystemTray.isSupported()) {
            System.out.println(“Não dá pra fazer, nem tenta!”);
            return;
        }

  //Instanciação de um objeto java.awt.PopupMenu  final PopupMenu pop = new PopupMenu();
  //Instanciação do objeto java.awt.SystemTray;  final SystemTray tray = SystemTray.getSystemTray();
  //Criação do objeto TrayIcon, informando uma imagem e um título  trayIcon = new TrayIcon(createImage(“padraoTray.jpg”, “É nois!!”));

  //Criação dos itens do Menu Popup  MenuItem item_1 = new MenuItem(“111111″);
  MenuItem item_2 = new MenuItem(“222222″);
  MenuItem item_3 = new MenuItem(“333333″);
  MenuItem item_4 = new MenuItem(“444444″);
  MenuItem item_5 = new MenuItem(“-sair-”);

  //Adicionando os itens ao Menu Popup  pop.add(item_1);
  pop.add(item_2);
  //Separador (linha que separa os itens no menu)  pop.addSeparator();
  pop.add(item_3);
  pop.add(item_4);
  //Separador (linha que separa os itens no menu)  pop.addSeparator();
  pop.add(item_5);

  //Setando o menu padrão no TrayIcon, que por acaso é este logo acima.  trayIcon.setPopupMenu(pop);

  //Adicionando o Icone na Area de Notificação, como o menu já está dentro do ícone,
  //irá junto também.

  try {
            tray.add(trayIcon);
        } catch (AWTException e) {
            System.out.println(“Não deu pra fazer isso…”);
            return;
        }
//Ação do botão “item_1″  item_1.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     //Simplesmente deixa-se a janela visível novamente.     pai.setVisible(true);
     //Esta linha deixa a janela sobre as outras, caso ela apareça minimizada.     pai.setExtendedState(JFrame.NORMAL);
try {
    //Agora basta remover (ou esconder) o ícone da Área de Notificação     tray.remove(trayIcon);
     trayIcon null;
     //Limpando a referência ao Systemtray da classe Janelinha     pai.an.finalize();
    } catch (Throwable e1) {
     e1.printStackTrace();
    }
    }
  });

  //Ação do botão “item_2″  item_2.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     JOptionPane.showMessageDialog(null“Você clicou no item 2″);
    }
  });

  //Ação do botão “item_3″  item_3.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     JOptionPane.showMessageDialog(null“Você clicou no item 3″);
    }
  });

  //Ação do botão “item_4″  item_4.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     JOptionPane.showMessageDialog(null“Você clicou no item 4″);
    }
  });

  //Ação do botão “item_5″  item_5.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     JOptionPane.showMessageDialog(null“Você clicou no item 5, este item é proibido, agora a aplicação terá que ser encerrada!!!”);
     JOptionPane.showMessageDialog(null“Escondam-se todos!!!!!”);
     System.exit(0);
    }
  });
}
protected static Image createImage(String path, String description) {
        URL imageURL = AreaNotificacao.class.getResource(path);
     
        if (imageURL == null) {
            System.err.println(“Caminho não encontrado: “ + path);
            return null;
        } else {
            return (new ImageIcon(imageURL, description)).getImage();
        }
    }
}
Toda a explicação aparentemente necessária está nos comentários ca classe, porém vamos à algumas observações.
Antes de executar qualquer coisa, e ocupar espaço à toa na memória, é feita uma verificação na JVM“if(!SystemTray.isSupported())”, se ela não suportar trabalhar com a classe SystemTray, então a brincadeira acaba.
Após isso são criados o Menu Popup e seus Itens, que no final ficam com essa cara:
Imagem da classe AreaNotificacao em ação, com o menu Popup sendo acessado
Com o Icone criado, e o Menu Popup Definido, é hora de colocar o bolo para assar, isso está sendo feito logo após a inclusão dos itens no menu, primeiro definindo o Menu Padrão do Icone “trayIcon.setPopupMenu(pop)”, e depois, dentro do bloclo TRY-CATCH o ícone está sendo inserido na Bandeja do Sistema “tray.add(trayIcon)”.
Está tudo funcionando, tudo muito bonito, mas os itens precisão de ações não é mesmo?
As ações que merecem um comentário em especial são as do item_1 e do item_5.
Na ação do item_1, que neste caso está exibindo a janela principal novamente, deve-se impreterivelmente remover e/ou limpar as variáveis responsáveis pelo Icone na Bandeja do Sistema, caso contrário em cada minimizada um novo ícone será criado e cedo ou tarde seu computador vai gritar pedindo socorro.
É possível notar também o acesso às variáveis/objetos estáticos (static) da classe Janelinha, definindo novos comportamentos para eles.
Na ação do item_5 é realizado um “salve-se quem puder”, mais conhecido como “System.exit(0)” que encerra a aplicação inteira, ou seja, tudo que estiver no mesmo espaço da JVM será encerrado, o ícone na bandeja e a propria “Janelinha”.
Acha que isso é tudo? Isso é só o começo!
Não poderia ser tão simples (a nível de conteúdo), queremos mais!
É possível entrar para o clube dos chatos e ficar exibindo aqueles balõezinhos que incomodam à todos.
Imagem do balão de avisos.
Para fazer essa mensagem chata, basta em algum momento, chamar a seguinte linha:
trayIcon.displayMessage(“Titulo”“Mensagem”tipo);
Trocando “Título” e “Mensagem”, obviamente pelos títulos e mensagens que serão exibidos no balão, e tipo por um dos seguintes tipos de mensagens:
TrayIcon.MessageType.ERROR
TrayIcon.MessageType.WARNING
TrayIcon.MessageType.INFO
TrayIcon.MessageType.NONE
Exemplos:
trayIcon.displayMessage(
“Atenção”,
“Perigo de causar o fim do mundo!”,
TrayIcon.MessageType.WARNING);
trayIcon.displayMessage(
“Alerta Vermelho!”,
“Este é um alerta de que o mundo está acabando”,
TrayIcon.MessageType.ERROR);
Agora que a parte facil já está aprendida, ou ao menos subentendida, agora é hora de partir para a parte difícil, adaptar a solução à um programa finalizado, mas para ajudar um pouco mais segue o link das classes de exemplo, utilizadas neste tutorial. Basta colocá-las no mesmo pacote e executar a classe “Janelinha”.
Boa sorte!

Nenhum comentário:

Postar um comentário