Beginning Sistemas de Información Java Menús Listado siguiente
PresentaciónEntrada/SalidaBibliotecasSwingMarcosBotonesCuadros de texto
ListasImágenesMenúsDiálogosArchivosGráficos 2D 

El uso de menús en Java es relativamente sencillo; de hecho es idéntico al uso de botones en cuanto a lo que a ActionListener se refiere. La diferencia entre opciones de menú y botones estriba en el modo en que se muestran los menús; en este sentido el uso de JMenuBar, JMenu y JMenuItem no resulta demasiado complejo. Véase a continuación un ejemplo trivial de uso de menús; este programa muestra en pantalla una cadena que depende de la opción de menú solicitada. El la opción Salir del menú Archivo da lugar realmente a la terminación del programa. Obsérvese que el procedimiento es análogo al seguido en un botón.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class UnMenu extends JFrame implements ActionListener {
 UnMenu() {
  super("Con un menú");
  setSize(300,200);
                addWindowListener(new WindowAdapter() {
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }
                });
                /* Método pedestre. Creamos el menú archivo */
  JMenu archivo = new JMenu("Archivo");
                /*Creamos múltiples opciones*/
  JMenuItem nuevo = new JMenuItem("Nuevo...");
  JMenuItem abrir = new JMenuItem("Abrir...");
  JMenuItem guardar = new JMenuItem("Guardar");
  JMenuItem guardarcomo = new JMenuItem("Guardar como...");
  JMenuItem cerrar = new JMenuItem("Cerrar");
  JMenuItem salir = new JMenuItem("Salir");
                /*Añadimos estas es al menú archivo*/
  archivo.add(nuevo);
  archivo.add(abrir);
  archivo.add(guardar);
  archivo.add(guardarcomo);
  archivo.add(cerrar);
  archivo.add(salir);
                /*Acción asociada*/
  nuevo.addActionListener(this);
  abrir.addActionListener(this);
  guardar.addActionListener(this);
  guardarcomo.addActionListener(this);
  cerrar.addActionListener(this);
  salir.addActionListener(this);
                /*Creamos el menú Edicion*/
                JMenu edicion = new JMenu("Edición");
                /*Creamos las opciones correspondientes*/
                JMenuItem deshacer = new JMenuItem("Deshacer");
                JMenuItem cortar = new JMenuItem("Cortar");
                JMenuItem copiar = new JMenuItem("Copiar");
                JMenuItem pegar = new JMenuItem("Pegar");
                JMenuItem borrar = new JMenuItem("Borrar");
                /*Las añadimos al menú de Edición*/
                edicion.add(deshacer);
                edicion.add(cortar);
                edicion.add(copiar);
                edicion.add(pegar);
                edicion.add(borrar);
                /*Acción asociada*/
                deshacer.addActionListener(this);
                cortar.addActionListener(this);
                copiar.addActionListener(this);
                pegar.addActionListener(this);
                borrar.addActionListener(this);
                /*Creamos la barra de menús*/
  mb = new JMenuBar();
                /*Añadimos los menús*/
  mb.add(archivo);
                mb.add(edicion);
 }
 
 public void actionPerformed(ActionEvent e) {
  /*Una forma de responder a los sucesos de menús*/
                if (e.getSource() instanceof JMenuItem)
                    {
                        String arg = e.getActionCommand();
                        System.out.println("Opción seleccionada: " + arg);
                        if (arg.equals("Salir")) System.exit(0);
                    }
 }
 public static void main(String[] args) {
  JFrame jf = new UnMenu();
  jf.setJMenuBar(mb);
  jf.show();
 }
 
 private static JMenuBar mb;
 
}
Para compilar este programa puede utilizarse el siguiente makefile:
program.jar: UnMenu.class UnMenu$$1.class
	jar cmfv manifest program.jar *.class

UnMenu.class: UnMenu.java
	javac UnMenu.java

UnMenu$$1.class: UnMenu.java
	javac UnMenu.java

clean:
	-rm *.jar
	-rm *.class
Obsérvese el mecanismo empleado en actionPerformed() para determinar el origen suceso que produjo su ejecución. En primer lugar, nos aseguramos de que el origen es un JMenuItem; este podría no ser el caso, porque también puede asociarse el ejemplar de ActionListener a un botón. A continuación, extraemos la cadena asociada al botón mediante una llamada a getActionCommand(); de este modo se podría, por ejemplo, seleccionar otro método asociado a ese botón concreto. Obsérvese que todas y cada una de las opciones de menú (todos los JMenuItem) dan lugar a la ejecución de un mismo actionPerformed() cuando son seleccionadas. Esto supone un cierto ahorro de memoria, a costa de hacer el proceso menos fácil de modificar. El procedimiento "puro" consiste en crear un ejemplar de ActionListener para cada opción de menú. Entonces la selección del método final asociado es automática, y la adición de nuevas opciones no requiere ni un solo cambio en el código de acción anterior. Este nuevo procedimiento es más complejo inicialmente, por cuanto requiere crear un mayor número de clases (preferiblemente en archivos independientes) pero posee la ventaja de admitir un tratamiento más eficiente cuando se construyen aplicaciones reales. Téngase en cuenta la posibilidad de desarrollo en paralelo por parte de múltiples personas. Téngase en cuenta también la ventaja de compilar únicamente las partes de programa que hayan sufrido cambios, posiblemente enmpleando make o algún sistema equivalente.
El nuevo aspecto que adquiere el programa principal es como sigue:
import javax.swing.JFrame;

public class SampleMenu
 {
  public static void main(String[] args)
   {
    JFrame jf;
    SampleModel sm = new SampleModel(33);
    SampleView sv = new SampleView(sm);
    jf = sv.getJFrame();
    jf.pack();
    jf.setVisible(true);
   }
 }
En el programa principal se crea un modelo sencillo al que se da valor a través de su constructor; este modelo puede ser más complejo sin modificar sustancialmente la interfaz. La vista considerada, como se verá, es una simple JLabel cuyo valor se hace cambiar a través de las acciones. El programa está construido tomando como base la arquitectura Modelo-Vista-Controlador, en donde el controlador el controlador serían las acciones, que necesitan tener acceso al modelo y a la vista. En la vista se tiene, claro, la interfaz gráfica de usuario:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SampleView {

 /* JFrame-related attributes */
 private WindowAdapter windowAdapter;
 private JFrame theJFrame;
 
 /* Model-related attributes */
 private SampleModel theModel;
 
 /* Menu-related attributes */
 private JMenuBar theJMenuBar;
 private JMenu fileMenu, editMenu;
 private JMenuItem newJMI, openJMI, saveJMI, saveAsJMI, closeJMI, exitJMI;
 private JMenuItem undoJMI, cutJMI, copyJMI, pasteJMI, deleteJMI;
 private ActionListener newAction, openAction, saveAction, saveAsAction, closeAction, exitAction;
 private ActionListener undoAction, cutAction, copyAction, pasteAction, deleteAction;
 /*
  This is the actual View
 */
 private JLabel jl;
 /*
  Constructor
 */
 SampleView(SampleModel sm)
  {
  
   /*
    Set up the model
   */
   theModel = sm;
   
   /* Set up the window */
   theJFrame = new JFrame("Working with menus");
   windowAdapter = new TheWindowAdapter();
   theJFrame.addWindowListener(windowAdapter);
   
   /*
    Set up the contents
   */
   JPanel jp = new JPanel();
   jl = new JLabel("Hi - this is a menu test!");
   jp.setBorder(BorderFactory.createEmptyBorder(  30, 100, 10,100));
   jp.add(jl);
   theJFrame.getContentPane().add(jp, BorderLayout.CENTER);
   
   /* We are ready to show the model contents */
   
   /* Set up the menus and menu bar */

   fileMenu = new JMenu("File");
   /* We create the File options */
   newJMI = new JMenuItem("New...");
   openJMI = new JMenuItem("Open...");
   saveJMI = new JMenuItem("Save");
   saveAsJMI = new JMenuItem("Save as...");
   closeJMI = new JMenuItem("Close");
   exitJMI = new JMenuItem("Quit");
   /* These we add to the File menu*/
   fileMenu.add(newJMI);
   fileMenu.add(openJMI);
   fileMenu.add(saveJMI);
   fileMenu.add(saveAsJMI);
   fileMenu.add(closeJMI);
   fileMenu.add(exitJMI);
   /* Now we create the Action classes */
   newAction = new NewAction(theModel,this);
   openAction = new OpenAction(theModel,this);
   saveAction = new SaveAction(theModel,this);
   saveAsAction = new SaveAsAction(theModel,this);
   closeAction = new CloseAction(theModel,this);
   exitAction = new ExitAction(theModel,this);
   /* Now we tell each option where it action method is */
   newJMI.addActionListener(newAction);
   openJMI.addActionListener(openAction);
   saveJMI.addActionListener(saveAction);
   saveAsJMI.addActionListener(saveAsAction);
   closeJMI.addActionListener(closeAction);
   exitJMI.addActionListener(exitAction);
   
   /* Now we do the same with the Edit menu */
   editMenu = new JMenu("Edit");
   /* We create the actions */
   undoAction = new UndoAction(theModel,this);
   cutAction = new CutAction(theModel,this);
   copyAction = new CopyAction(theModel,this);
   pasteAction = new PasteAction(theModel,this);
   deleteAction = new DeleteAction(theModel,this);
   /* We create the options*/
   undoJMI = new JMenuItem("Undo");
   cutJMI = new JMenuItem("Cut");
   copyJMI = new JMenuItem("Copy");
   pasteJMI = new JMenuItem("Paste");
   deleteJMI = new JMenuItem("Delete");
   /*Now we tell each option where its action method is*/
   undoJMI.addActionListener(undoAction);
   cutJMI.addActionListener(cutAction);
   copyJMI.addActionListener(copyAction);
   pasteJMI.addActionListener(pasteAction);
   deleteJMI.addActionListener(deleteAction);
   /* Add the JMenuItems to the Edit menu*/
   editMenu.add(undoJMI);
   editMenu.add(cutJMI);
   editMenu.add(cutJMI);
   editMenu.add(pasteJMI);
   editMenu.add(deleteJMI);
   /* Finally we create a menu bar*/
   theJMenuBar = new JMenuBar();
   /* Then we add the menus to the menu bar */
   theJMenuBar.add(fileMenu);
   theJMenuBar.add(editMenu);
   theJFrame.setJMenuBar(theJMenuBar);
  }
 
 public JFrame getJFrame()
  {
   return theJFrame;
  }
 public JLabel getJLabel()
  {
   return jl;
  }
}
La vista está dotada de los métodos elementales de acceso necesarios; en particular, getJLabel() va a ser la vía de acceso a la vista desde el controlador (las acciones). El programa principal actúa sobre la vista a través de getJFrame(), aunque se limita a hacer visible el JFrame. Las acciones tienen acceso al modelo a través de su constructor, mediante el cual reciben tanto la vista como el modelo. Véase como ejemplo una de las clases de acción:
import java.awt.*;
import java.awt.event.*;
/*
 This is a sample action class. It gets a model through
 its constructor.
*/
public class CloseAction implements ActionListener {
 SampleModel theModel;
  SampleView theView;

 CloseAction(SampleModel sm, SampleView tv)
  {
   theModel = sm;
   theView = tv;
  }
 public void actionPerformed(ActionEvent ae)
  {
   theView.getJLabel().setText(ae.getActionCommand()+theModel.getDatum());
  }
}
Este archivo comprimido contiene la totalidad de las clases necesarias para la compilación de este programa. En un programa "real" se dotaría al modelo de una estructura de datos más compleja, con métodos de acceso manejados desde las acciones para que los cambios efectuados en los datos se reflejaran en la interfaz de usuario que ofrece la vista.
Si se emplea una arquitectura de directorios más real, será conveniente separar las clases correspondientes al modelo, la vista y el controlador. Esta nueva arquitectura, basada en paquetes, puede observarse en este archivo comprimido, que contiene ya todos los directorios y programas necesarios. Obsérvese la presencia del siguiente archivo, llamado file_list:
model/SampleModel.java
view/TheWindowAdapter.java
view/SampleView.java
action/UndoAction.java
action/PasteAction.java
action/ExitAction.java
action/DeleteAction.java
action/CopyAction.java
action/CutAction.java
action/SaveAsAction.java
action/OpenAction.java
action/NewAction.java
action/SaveAction.java
action/CloseAction.java
SampleMenu.java
Como puede apreciarse, es una relación de los archivos de que consta el programa. Se ha creado para hacer posible la compilación cómoda de todas las clases de que consta el programa, empleando una expresión como la siguiente:
javac @file_list
Esto hace fácil compilar todo, para después crear un archivo jar mediante la expresión
jar cmfv manifest program.jar SampleMenu.class model/*.class view/*.class action/*.class
Se pierde la flexibilidad de make, pero se gana en sencillez y posiblemente en velocidad de compilación. El contenido de manifest es el esperado:
Main-Class: SampleMenu

Ejercicios propuestos

  1. Se dispone de un archivo de texto (fijo y de nombre conocido) formado por dos columnas de números reales, separados mediante un tabulador. Se pide construir un programa capaz de leer el archivo, mostrar su contenido en dos columnas de JTextField y permitir guardar de nuevo el archivo, con idéntico formato, tras haber efectuado modificaciones en los datos. (De otro modo: consultar el tutorial de JTable. Si se utiliza un JTable, las opciones de Cortar/Copiar/Pegar serán automáticas). Utilizar la posibilidad de seleccionar archivos, empleando JFileChooser para seleccionar tanto el archivo leído como el escrito.
  2. Se dispone de un fichero formado por nombres, apellidos, teléfonos y edades, separados mediante asteriscos. Se pide construir un programa adecuado para leer archivos como los anteriores, mostrando su contenido en columnas (de JTextField o en una JTable). El programa debe permitir hacer modificaciones en los datos y volver a almacenar la información con formato legible posteriormente (también con asteriscos como separadores). Dotar al programa de la posibilidad de ordenar los registros por cualquier campo, ofreciendo las oportunas opciones (menú Ordenar, con opciones Por nombre, Por apellidos, Por teléfono y Por edad).