¿Cómo creo un menú contextual de clic derecho en Java Swing?

110

Actualmente estoy creando un menú contextual de clic derecho al crear una instancia de un nuevo JMenuclic derecho y establecer su ubicación en la posición del mouse ... ¿Hay una mejor manera?

Wayne
fuente

Respuestas:

140

Probablemente esté llamando manualmente setVisible(true) al menú. Eso puede causar un comportamiento desagradable con errores en el menú.

El show(Component, int x, int x)método maneja todas las cosas que necesita que sucedan (resaltar las cosas al pasar el mouse y cerrar la ventana emergente cuando sea necesario) donde usar setVisible(true)solo muestra el menú sin agregar ningún comportamiento adicional.

Para crear un menú emergente de clic derecho, simplemente cree un archivo JPopupMenu.

class PopUpDemo extends JPopupMenu {
    JMenuItem anItem;
    public PopUpDemo() {
        anItem = new JMenuItem("Click Me!");
        add(anItem);
    }
}

Luego, todo lo que necesita hacer es agregar un elemento personalizado MouseListenera los componentes para los que desea que aparezca el menú.

class PopClickListener extends MouseAdapter {
    public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger())
            doPop(e);
    }

    public void mouseReleased(MouseEvent e) {
        if (e.isPopupTrigger())
            doPop(e);
    }

    private void doPop(MouseEvent e) {
        PopUpDemo menu = new PopUpDemo();
        menu.show(e.getComponent(), e.getX(), e.getY());
    }
}

// Then on your component(s)
component.addMouseListener(new PopClickListener());

Por supuesto, los tutoriales tienen una explicación un poco más detallada .

Nota: Si nota que el menú emergente aparece muy alejado de donde el usuario hizo clic, intente usar e.getXOnScreen()ye.getYOnScreen() métodos para las coordenadas x e y.

jjnguy
fuente
Después de usar el código anterior, aparece el error que dice que "El método addMouseListener (MouseListener) en el tipo Figure no es aplicable para los argumentos (PopClickListener)" Saludos, Vinay
1
@ user1035905 ¿Te aseguraste de que se PopClickListenerextienda MouseAdapter?
jjnguy
¿Cómo se consigue que funcione con la tecla del menú contextual del teclado?
Christoffer Hammarström
el único caso en el que esta solución es mejor que la de kleopatra es cuando necesita que suceda alguna lógica personalizada (por ejemplo, diferentes menús emergentes en diferentes condiciones); aún así, debe agregar un oyente de teclado para trabajar con la tecla del menú contextual
2
¿Qué significa component?
Loint
117

Esta pregunta es un poco antigua, al igual que las respuestas (y también el tutorial)

La api actual para configurar un menú emergente en Swing es

myComponent.setComponentPopupMenu(myPopupMenu);

De esta forma se mostrará automágicamente, tanto para los disparadores del ratón como del teclado (este último depende de LAF). Además, admite la reutilización de la misma ventana emergente en los elementos secundarios de un contenedor. Para habilitar esa función:

myChild.setInheritsPopupMenu(true);
Cleopatra
fuente
2
@ user681159 no conoce ninguno, y no es necesario, en mi opinión, simplemente lea el documento de la API :-)
kleopatra
2
¿Cómo usaría esto con un JTablepara que aparezca en la fila seleccionada o en la fila donde hace clic derecho? ¿O en este escenario el método antiguo es el que debe elegirse?
Alex Burdusel
1
@Burfee eso o mejorar JTable a través de subclases: anule getPopupLocation (..) y almacene la ubicación para su uso posterior, consulte un control de calidad reciente que se implementa en todos los componentes de la colección
SwingX
18

Hay una sección sobre cómo abrir un menú emergente en el artículo Cómo usar los menús de los tutoriales de Java que explica cómo usar la JPopupMenuclase.

El código de ejemplo en el tutorial muestra cómo agregar correos MouseListenerelectrónicos a los componentes que deberían mostrar un menú emergente, y muestra el menú en consecuencia.

(El método que describe es bastante similar a la forma en que el tutorial presenta la forma de mostrar un menú emergente en un componente).

coobird
fuente
8

El siguiente código implementa un menú contextual predeterminado conocido por las Windowsfunciones de copiar, cortar, pegar, seleccionar todo, deshacer y rehacer. También funciona en Linuxy Mac OS X:

import javax.swing.*;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class DefaultContextMenu extends JPopupMenu
{
    private Clipboard clipboard;

    private UndoManager undoManager;

    private JMenuItem undo;
    private JMenuItem redo;
    private JMenuItem cut;
    private JMenuItem copy;
    private JMenuItem paste;
    private JMenuItem delete;
    private JMenuItem selectAll;

    private JTextComponent textComponent;

    public DefaultContextMenu()
    {
        undoManager = new UndoManager();
        clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

        addPopupMenuItems();
    }

    private void addPopupMenuItems()
    {
        undo = new JMenuItem("Undo");
        undo.setEnabled(false);
        undo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        undo.addActionListener(event -> undoManager.undo());
        add(undo);

        redo = new JMenuItem("Redo");
        redo.setEnabled(false);
        redo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        redo.addActionListener(event -> undoManager.redo());
        add(redo);

        add(new JSeparator());

        cut = new JMenuItem("Cut");
        cut.setEnabled(false);
        cut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        cut.addActionListener(event -> textComponent.cut());
        add(cut);

        copy = new JMenuItem("Copy");
        copy.setEnabled(false);
        copy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        copy.addActionListener(event -> textComponent.copy());
        add(copy);

        paste = new JMenuItem("Paste");
        paste.setEnabled(false);
        paste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        paste.addActionListener(event -> textComponent.paste());
        add(paste);

        delete = new JMenuItem("Delete");
        delete.setEnabled(false);
        delete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        delete.addActionListener(event -> textComponent.replaceSelection(""));
        add(delete);

        add(new JSeparator());

        selectAll = new JMenuItem("Select All");
        selectAll.setEnabled(false);
        selectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        selectAll.addActionListener(event -> textComponent.selectAll());
        add(selectAll);
    }

    private void addTo(JTextComponent textComponent)
    {
        textComponent.addKeyListener(new KeyAdapter()
        {
            @Override
            public void keyPressed(KeyEvent pressedEvent)
            {
                if ((pressedEvent.getKeyCode() == KeyEvent.VK_Z)
                        && ((pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0))
                {
                    if (undoManager.canUndo())
                    {
                        undoManager.undo();
                    }
                }

                if ((pressedEvent.getKeyCode() == KeyEvent.VK_Y)
                        && ((pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0))
                {
                    if (undoManager.canRedo())
                    {
                        undoManager.redo();
                    }
                }
            }
        });

        textComponent.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent releasedEvent)
            {
                handleContextMenu(releasedEvent);
            }

            @Override
            public void mouseReleased(MouseEvent releasedEvent)
            {
                handleContextMenu(releasedEvent);
            }
        });

        textComponent.getDocument().addUndoableEditListener(event -> undoManager.addEdit(event.getEdit()));
    }

    private void handleContextMenu(MouseEvent releasedEvent)
    {
        if (releasedEvent.getButton() == MouseEvent.BUTTON3)
        {
            processClick(releasedEvent);
        }
    }

    private void processClick(MouseEvent event)
    {
        textComponent = (JTextComponent) event.getSource();
        textComponent.requestFocus();

        boolean enableUndo = undoManager.canUndo();
        boolean enableRedo = undoManager.canRedo();
        boolean enableCut = false;
        boolean enableCopy = false;
        boolean enablePaste = false;
        boolean enableDelete = false;
        boolean enableSelectAll = false;

        String selectedText = textComponent.getSelectedText();
        String text = textComponent.getText();

        if (text != null)
        {
            if (text.length() > 0)
            {
                enableSelectAll = true;
            }
        }

        if (selectedText != null)
        {
            if (selectedText.length() > 0)
            {
                enableCut = true;
                enableCopy = true;
                enableDelete = true;
            }
        }

        if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor) && textComponent.isEnabled())
        {
            enablePaste = true;
        }

        undo.setEnabled(enableUndo);
        redo.setEnabled(enableRedo);
        cut.setEnabled(enableCut);
        copy.setEnabled(enableCopy);
        paste.setEnabled(enablePaste);
        delete.setEnabled(enableDelete);
        selectAll.setEnabled(enableSelectAll);

        // Shows the popup menu
        show(textComponent, event.getX(), event.getY());
    }

    public static void addDefaultContextMenu(JTextComponent component)
    {
        DefaultContextMenu defaultContextMenu = new DefaultContextMenu();
        defaultContextMenu.addTo(component);
    }
}

Uso:

JTextArea textArea = new JTextArea();
DefaultContextMenu.addDefaultContextMenu(textArea);

Ahora textAreatendrá un menú contextual cuando se haga clic con el botón derecho.

BullyWiiPlaza
fuente
Gran solucion Una cosa: podría / debería usar en releasedEvent.isPopupTrigger()lugar de releasedEvent.getButton() == MouseEvent.BUTTON3funcionar correctamente en todas las plataformas.
Frederic Leitenberger
Un error más en el escucha de claves: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()estos deben ser ambos Exo no Ex. La Exversión de getMenuShortcutKeyMask()solo está disponible desde java 10+.
Frederic Leitenberger
1

Corregiré el uso de ese método que sugirió @BullyWillPlaza. La razón es que cuando trato de agregar add textArea solo a contextMenu, no es visible, y si lo agrego tanto a contextMenu como a algún panel, se encuentra: Diferente asociación doble de padres si trato de cambiar al editor de diseño.

TexetObjcet.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            if (SwingUtilities.isRightMouseButton(e)){
                contextmenu.add(TexetObjcet);
                contextmenu.show(TexetObjcet, 0, 0);
            }
        }
    }); 

Haga que el mouse escuche como este para el objeto de texto que necesita tener una ventana emergente. Lo que esto hará es que cuando haga clic derecho en su objeto de texto, agregará esa ventana emergente y la mostrará. De esta manera no encontrará ese error. La solución que hizo @BullyWillPlaza es muy buena, rica y rápida de implementar en su programa, por lo que debería probarla para ver cómo le gusta.

Đumić Branislav
fuente
Además, no olvide que todavía necesita importar ese contextMenu y crear una nueva instancia.
Đumić Branislav