JPopupMenu : Menu en un JComponent

De ChuWiki

Vamos a ver un pequeño ejemplo de cómo añadir un menú (un JPopupMenu) a cualquier JComponent de Java. Es posible añadirlo también a cualquier Component, pero en mi caso he tenido problemas con java.awt.Canvas, mientras que con los JComponent funcionan bien.


Acciones para JPopupMenu[editar]

Primero creamos las acciones para nuestro menú, cada uno de los items. Para ello, hacemos clases que implementen Action o para facilitar la tarea, que hereden de AbstractAction. En el ejemplo vamos a hacer una que podamos instanciar fácilmente varias veces.

package com.chuidiang.ejemplos.popup_menu;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;

/**
 * Accion generica para los items del menu.
 * 
 * @author Chuidiang
 * 
 */
public class AccionMenu extends AbstractAction {
	private String textoOpcion;

	/**
	 * Se le pasa el nombre que se quiere que se muestre
	 * 
	 * @param textoOpcion
	 */
	public AccionMenu(String textoOpcion) {
		this.textoOpcion = textoOpcion;
		this.putValue(Action.NAME, textoOpcion);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println("Pulsado " + textoOpcion);
	}
}

Simplemente admite el texto que será mostrado en el JPopupMenu. Al pulsar, saca dicho texto por pantalla. Con esta clase, instanciándola varias veces, podemos hacer varios items de JPopupMenu, simplemente pasando nombres distintos en el constructor.

El JPopupMenu[editar]

El componente java para el menú es un JPopupMenu. Sólo hay que instanciarlo y por medio del método add() pasarle las acciones que queremos que sean nuestros items del menú. Por supuesto, hay más métodos para añadir las acciones de otra forma o para añadir incluso submenus.

// Construccion del JPopupMenu
popup = new JPopupMenu();
popup.add(new AccionMenu("uno"));
popup.add(new AccionMenu("dos"));
popup.add(new AccionMenu("tres"));


Añadir el MouseListener[editar]

Luego hay que añadir un addMouseListener() al componente java que queramos que tenga nuestro JPopupMenu. En nuestro ejemplo, será el JLabel, al que hemos llamado, para ser más genérico, cualquierJComponent.

cualquierJComponent.addMouseListener(new MouseListener() {
   ...
}


Mostrar el JPopupMenu[editar]

Para decidir con qué evento concreto de ratón debemos visualizar el menú, el MouseEvent que recibimos tiene un método isPopupTrigger(). Este método devolverá true si el evento de ratón es el que tiene el sistema operativo por defecto para mostrar los menús emergentes. De esta forma, nuestra aplicación mostrará el menú con el mismo evento de ratón que el resto de las aplicaciones del sistema operativo.

En el caso de Windows Vista, el evento es de "mouseReleased" con el botón derecho. En otros sistemas operativos no lo podemos saber. Por ello es aconsejable implementar TODOS los métodos de MouseListener.

Finalmente, para indicar en que posición de pantalla queremos que se muestre el menú, debemos llamar a su método setLocation(). La ubicación habitual suele ser donde se ha producido el evento de ratón. Para obtener esta posición tenemos el método getLocationOnScreen() de MouseEvent.

Resumiendo, el código para mostrar el menú podría ser así

private void muestraMenu(MouseEvent e) {
   if (e.isPopupTrigger()) {
      popup.setLocation(e.getLocationOnScreen());
      popup.setVisible(true);
   }
}


El ejemplo completo[editar]

Junto a la clase AccionMenu que pusimos al principio, el siguiente código puede ser un ejemplo completo que muestra una ventana con un JLabel. El JLabel tendrá el JPopupMenu con tres opciones "tontas".

package com.chuidiang.ejemplos.popup_menu;

import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.WindowConstants;

/**
 * Ejemplo de JPopupMenu. Muestra una ventana con un JLabel sobre el que se
 * puede desplegar un menú.
 * 
 * @author Chuidiang
 * 
 */
public class EjemploPopUpMenu {

	/**
	 * El JPopupMenu. Se hace estático para poder acceder a él desde el
	 * MouseListener.
	 */
	private static JPopupMenu popup;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		// Construccion y visualizacion de la ventana.
		JFrame v = new JFrame("Ejemplo JPopupMenu");
		JComponent cualquierJComponent = new JLabel("Tengo un menú");
		v.getContentPane().add(cualquierJComponent);
		v.pack();
		v.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		v.setVisible(true);

		// Construccion del JPopupMenu
		popup = new JPopupMenu();
		popup.add(new AccionMenu("uno"));
		popup.add(new AccionMenu("dos"));
		popup.add(new AccionMenu("tres"));

		// Se añade el JPopupMenu al JLabel.
		// Debemos implementar todos los metodos de MouseListener porque
		// no sabemos a priori que evento usara el sistema operativo para
		// mostrar los menus.
		cualquierJComponent.addMouseListener(new MouseListener() {

			@Override
			public void mouseReleased(MouseEvent e) {
				muestraMenu(e);
			}

			/**
			 * Método que muestra el menú.
			 * 
			 * @param e
			 */
			private void muestraMenu(MouseEvent e) {
				// isPopupTrigger() indica si es el evento de raton
				// por defecto en el sistema operativo para mostrar
				// el menu.
				if (e.isPopupTrigger()) {
					popup.setLocation(e.getLocationOnScreen());
					popup.setVisible(true);
				}
			}

			@Override
			public void mousePressed(MouseEvent e) {
				muestraMenu(e);
			}

			@Override
			public void mouseExited(MouseEvent e) {
				muestraMenu(e);
			}

			@Override
			public void mouseEntered(MouseEvent e) {
				muestraMenu(e);
			}

			@Override
			public void mouseClicked(MouseEvent e) {
				muestraMenu(e);
			}

		});
	}

}