Desenvolver menus em Java é extremamente simples, mas ao mesmo tempo cansativo e requer uma certa quantidade de código repetitiva. O objetivo deste artigo é mostrar uma maneira (das muitas existentes) de se montar menus de uma forma dinâmica.

Obs.: é necessário ter a noção mais básica de herança e do framework de coleções do Java para entender o funcionamento das classes criadas para este artigo.

Montagem manual de um menu

Antes de introduzir a classe que irá produzir o menu é necessário entender como funciona a montagem do mesmo.

As classes principais do artigo são: JMenuBar, que é a barra de menu visualizada no topo da aplicação; JMenu que são os menus propriamente ditos e que estão inseridos na barra de menu, por exemplo: Arquivo, Editar, Exibir, etc...; e por fim JMenuItem que são os itens internos de um menu. Por exemplo, dentro de Arquivo você encontra: Novo, Abrir, Salvar, etc... todas elas pertencentes ao pacote javax.swing.*;

A listagem 1 mostra a criação de um menu contendo algumas opções que geralmente são encontradas em editores de texto.


//imports aqui
//declaração de classe
//métodos adicionais
public void montaMenu(){
 //criação da barra de menu
 JMenuBar jMenuBar = new JMenuBar();
 
 //criação dos menus
 JMenu jMenuArquivo = new JMenu("Arquivo");
 JMenu jMenuEditar = new JMenu("Editar");
 JMenu jMenuExibir = new JMenu("Editar");
 JMenu jMenuFormatar = new JMenu("Formatar");
 JMenu jMenuAjuda = new JMenu("Ajuda");
 //criação dos itens do menu arquivo
 JMenuItem jMenuItemNovo = new JMenuItem("Novo");
 JMenuItem jMenuItemAbrir = new JMenuItem("Abrir");
 JMenuItem jMenuItemSalvar = new JMenuItem("Salvar");
 JMenuItem jMenuItemSalvarComo = new JMenuItem("Salvar Como");
 JMenuItem jMenuItemSair = new JMenuItem("Sair");
 
 //adicionando menus à barra de menu
 jMenuBar.add(jMenuArquivo);
 jMenuBar.add(jMenuEditar);
 jMenuBar.add(jMenuExibir);
 jMenuBar.add(jMenuFormatar);
 jMenuBar.add(jMenuAjuda);
 
 //adicionando itens de menu ao menu arquivo
 jMenuArquivo.add(jMenuItemNovo);
 jMenuArquivo.addSeparator();
 jMenuArquivo.add(jMenuItemAbrir);
 jMenuArquivo.add(jMenuItemSalvar);
 jMenuArquivo.add(jMenuItemSalvarComo);
 jMenuArquivo.addSeparator();
 jMenuArquivo.add(jMenuItemSair);
 
 //adicionando evento ao item do menu arquivo, sair
 jMenuItemSair.addActionListener(new ActionListener(){
 public void actionPerformed(ActionEvent e){
 JOptionPane.showMessageDialog(null, "Você saiu!");
 }
 });
 
 //adicionando a barra de menu ao aplicativo
 super.setJMenuBar(jMenuBar);
}

É possível perceber que o desenvolver precisa criar uma série de objetos de JMenu e JMenuItem, adicionar cada item de menu a seu menu específico de forma que para grandes aplicações isto acaba em uma quantidade enorme de linhas de código.

No final da Listagem 01 há um tratamento de evento para o item sair. E a chamada super.setJMenuBar(jMenuBar); é devido ao fato da classe herdar JFrame. É nesse momento que aplicação recebe o menu pronto e o adiciona à sua interface gráfica.

Para evitar uma grande quantidade de código nessa parte do artigo, foi poupado de se criar os itens de menu para os menus restantes, como o ‘Editar’. Mas pare um pouco pra pensar em como ficaria caso houvesse muitos itens para cada um dos menus criados.

Como dito no início do artigo, a maneira de se montar um menu dinâmico a ser apresentada aqui é uma das muitas possíveis. Há Frameworks que fazem todo o trabalho de criação e configuração do mesmo, inclusive ferramentas em alguns IDEs. Mas se você veio até aqui é porque tem interesse em ver como é possível uma classe fazer o “serviço braçal”! Então vamos lá.

Desenvolvendo a classe Menu

Vamos então ao que interessa! Precisamos de uma classe que poupe o desenvolvedor de escrever as mesmas chamadas de métodos diversas vezes. O caminho escolhido para isso foi trabalhar com algumas classes e interfaces do framework de coleções presentes no Java.

Encapsulando as chamadas de métodos de adição de menus e itens de menu será possível poupar espaço e tempo, e é aí que entram as coleções. As classes de adição irão trabalhar baseando-se em iteradores.


import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
	public class Menu extends JMenuBar{
	HashMap mapJMenus = new HashMap();
	public void addJMenus(Iterator itMenus){
	Iterator it = itMenus;
		 while(it.hasNext()){
		 String titulo = it.next();
		 JMenu menu = new JMenu(titulo);
		 this.add(menu);
		 mapJMenus.put(titulo, menu);
		}
	}
}
Listagem 02. O método addJMenus

A classe Menu herda JMenuBar. Inicialmente cria-se um HashMap responsável por manter, em tempo de execução, todos os menus adicionados.

O método addJMenus recebe um Iterator de Strings que são os nomes dos menus. Fica então a pergunta: por que não usar ArrayList, Vector ou outras classes do tipo? Essa técnica permite que as classes que irão instanciar Menu para criação de seus menus sejam livres para utilizar qualquer forma de coleções que suportem iteradores, de forma que ao chamar addJMenus eles precisem apenas enviar um iterador de seus elementos.

É dentro do bloco while, então, que se cria o JMenu com os títulos contidos no iterador. A chamada this.add(menu) adiciona o menu à barra de menus (vale lembrar que a classe Menu herda JMenuBar). Em seguida adiciona-se o menu criado ao HashMap de menus.

Menus criados. Agora é hora de tratar os itens de menu:


HashMap> mapJMenuItens = new HashMap>();
public void addJMenuItems(String menu, Iterator itens) throws NullPointerException{
HashMap mapItens = new HashMap();
Iterator it = itens;
JMenu jMenu = this.getJMenu(menu);
	if(!mapJMenus.containsKey(menu)){
	 String descricaoNullPointer = "O menu '" + menu + "' não existe.";
	 throw new NullPointerException(descricaoNullPointer);
	}
	while(it.hasNext()){
		 String titulo = it.next();
		 if(titulo.equals("")){
		 jMenu.addSeparator();
		 continue;
	 }
	 JMenuItem menuItem = new JMenuItem(titulo);
	 jMenu.add(menuItem);
	 mapItens.put(titulo, menuItem);
	}
mapJMenuItens.put(menu,mapItens);
}
Listagem 03. O método addJMenuItems

A função do HashMap mapJMenuItens é a mesma do criado na Listagem 03, porém, esse mantém os itens de menu em tempo de execução.

O método addJMenuItem recebe o nome do menu ao qual serão adicionados os itens e um Iterator contendo os nomes desses itens.

Há um bloco if antes das tarefas do método onde testa-se se há um menu com o nome passado. Se não houver ele lança uma exceção, nesse caso, uma NullPointerException.

O método então inicia seus trabalhos, de forma semelhante ao addJMenus, porém agora ele conta com um bloco if dentro do while, que adiciona um separador de itens de menu caso o nome esteja vazio. Após a iteração com os nomes dos itens é adicionado ao HashMap o nome do menu e um outro HashMap contendo os itens de menu.

Para possibilitar ao desenvolvedor trabalhar com um menu ou item de menu criado por Menu, define-se dois métodos de acesso que retornam JMenu e JMenuItem respectivamente, como indica a Listagem 04:


public JMenu getJMenu(String titulo){
return mapJMenus.get(titulo);
}
public JMenuItem getJMenuItem(String menu, String item){
HashMap map = mapJMenuItens.get(menu);
return map.get(item);
Listagem 04. Métodos de acesso

Com esses métodos é possível obter qualquer objeto criado pela classe e alterar suas propriedades específicas como setEnabled, setText, etc...

Finalmente iremos tratar os eventos dos itens de menu. Como há os métodos de acesso getJMenu e getJMenuItem, o desenvolvedor pode fazer a adição dos eventos manualmente na classe principal do aplicativo. Há também a possibilidade de automatizar essa tarefa, o que será feito agora:


public void addEventos(String menu,Iterator itens,Iterator eventos){
HashMap map = mapJMenuItens.get(menu);
while(itens.hasNext()){
 String item = itens.next();
 if(item.equals("")){
 continue;
 }
 JMenuItem menuItem = map.get(item);
 menuItem.addActionListener(eventos.next());
}
Listagem 05. Adicionando eventos em lote

O método addEventos recebe uma String que é o nome do menu, um Iterator com os nomes dos itens de menu e finalmente outro Iterator contendo os eventos.

É necessário ressaltar que os eventos contidos no iterador serão adicionados na mesma ordem que forem lidos os nomes dos itens de menu, de forma que se houver quantidades diferentes de elementos nos dois iteradores a adição de eventos não se comportará corretamente. Seria interessante tratar essa possibilidade, mas isso fugiria um pouco do objetivo do artigo que é mostrar claramente a automatização do processo de criação de menus.

OBS.: caso o desenvolvedor quisesse utilizar o mesmo iterador dos itens de menu usado na criação para enviar ao método addEventos acarretaria em uma exceção, pois ele contém strings vazias, que são os separadores de menu. Para evitar isso, colocou-se um bloco if no interior do método que pula o processo de adição de eventos caso essa String vazia esteja contida no iterador.

Por fim, ainda no bloco while, o método faz a adição dos eventos.

O código-fonte, comentado e montado está disponível para download.

Utilizando a classe Menu em um aplicativo

Agora que temos nossa classe para montagem de menus, poderemos instanciá-la em qualquer aplicação daqui pra frente. A Listagem 07 demonstra um menu, um pouco maior que o primeiro mostrado no começo do artigo e com uma menor quantidade de código.


//imports aqui...
public class Aplicacao extends JDialog{
Menu menu = new Menu();
private Iterator toArrayList(Object[] itens){
 ArrayList list = new ArrayList();
 for(int i = 0; i < itens.length; i++){
 list.add(itens[i]);
 }
 return list.iterator();
 }
Listagem 6. O método toArrayList

Antes de prosseguirmos, o método toArrayList “converte” um vetor de objetos em um Iterator que é a interface reconhecida pelos métodos de Menu.


private void montaMenu(){
String[] jMenus = {"Arquivo", "Editar", "Exibir", "Formatar", "Ajuda"};
String[] arquivoItens = {"Novo", "Abrir", "" ,"Salvar", "Salvar Como", "", "Fechar", 
"Fechar Todos", "", "Sair" };
String[] editarItens = {"Selecionar Tudo", "", "Localizar" ,"Localizar e Substituir" };
ActionListener[] eventosEditar = {new EvtSelecionarTudo(),new EvtLocalizar(),
new EvtLocalizarESubstituir()};
 menu.addJMenus(toArrayList(jMenus));
 menu.addJMenuItems("Arquivo", toArrayList(arquivoItens));
 menu.addJMenuItems("Editar", toArrayList(editarItens));
menu.addEventos("Editar", toArrayList(editarItens), toArrayList(eventosEditar));
menu.getJMenuItem("Arquivo", "Sair").addActionListener(new EvtSair());
}
Listagem 7. O método montaMenu

O método montaMenu inicialmente cria os vetores contendo os nomes dos menus, itens de menus para arquivo e editar respectivamente, e um vetor de eventos. Em seguida chama addJMenus, que irá criar a barra de menu da aplicação; addJMenuItems duas vezes – a primeira para adicionar os itens do menu arquivo e a segunda do menu editar; addEventos para configurar os eventos dos itens do menu editar.

A penultima linha configura um evento isolado para demonstrar o tratamento personalizado de objetos contidos no menu. O método getJMenuItem retornou o item sair, e em seguida adicionou-se o evento responsável por fechar a aplicação.

Compare o método montaMenu listado no início do artigo com esta nova versão. Atente-se para o detalhe de que no primeiro, só adicionou-se os itens do menu arquivo, sendo que neste último, adicionou-se também os itens de editar com seus eventos.

OBS.: o vetor de eventos contém instâncias de classes internas não listadas no artigo, como EvtSelecionarTudo, EvtLocalizar, etc. As mesmas estão presentes no código-fonte que está disponível para download, assim como o método main e o restante da classe, onde há algumas chamadas de método responsáveis por configurar o JDialog para que se tenha uma aplicação funcionando.

Conclusão

As capacidades de um menu são muito amplas e fogem ao escopo deste artigo que mostra apenas a criação e configuração básica do mesmo. Perceba que com a classe desenvolvida, para se criar sub-menus é necessário utilizar os métodos de acesso e fazê-lo manualmente, por exemplo. Mas o objetivo deste meu primeiro artigo é apenas dar início à essa classe de automação, sendo possível estendê-la a novas funções, procurando sempre alcançar a gama de possibilidades presentes nas classes de menus fornecidas pelo Java.