Comment créer un menu contextuel clic droit dans Java Swing?

110

Je crée actuellement un menu contextuel clic droit en instanciant un nouveau JMenuclic droit et en définissant son emplacement sur celui de la position de la souris ... Y a-t-il un meilleur moyen?

Wayne
la source

Réponses:

140

Vous appelez probablement manuellement setVisible(true)sur le menu. Cela peut provoquer un comportement de buggy désagréable dans le menu.

La show(Component, int x, int x)méthode gère toutes les choses dont vous avez besoin (surligner les choses au survol de la souris et fermer la fenêtre contextuelle si nécessaire) où l'utilisation setVisible(true)montre simplement le menu sans ajouter de comportement supplémentaire.

Pour créer un menu contextuel clic droit, créez simplement un fichier JPopupMenu.

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

Ensuite, tout ce que vous avez à faire est d'ajouter une personnalisation MouseListeneraux composants pour lesquels vous souhaitez que le menu apparaisse.

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());

Bien sûr, les tutoriels ont une explication un peu plus approfondie .

Remarque: Si vous remarquez que le menu contextuel apparaît loin de l'endroit où l'utilisateur a cliqué, essayez d'utiliser les méthodes e.getXOnScreen()et e.getYOnScreen()pour les coordonnées x et y.

jjnguy
la source
Après avoir utilisé le code ci-dessus, j'obtiens l'erreur disant que "La méthode addMouseListener (MouseListener) dans le type Figure n'est pas applicable pour les arguments (PopClickListener)" Cordialement, Vinay
1
@ user1035905 Vous êtes-vous assuré que l' PopClickListenerextension s'étend MouseAdapter?
jjnguy
Comment le faire fonctionner avec la touche de menu contextuel du clavier?
Christoffer Hammarström
le seul cas dans lequel cette solution est meilleure que celle de kleopatra est lorsque vous avez besoin d'une logique personnalisée (par exemple, différents menus contextuels dans des conditions différentes); encore, vous devez ajouter un écouteur de clavier pour travailler avec la touche de menu contextuel
2
que signifie component?
Loint
117

Cette question est un peu ancienne - tout comme les réponses (et le tutoriel aussi)

L'API actuelle pour définir un menu contextuel dans Swing est

myComponent.setComponentPopupMenu(myPopupMenu);

De cette façon, il sera affiché automatiquement, à la fois pour les déclencheurs de souris et de clavier (ce dernier dépend de LAF). De plus, il prend en charge la réutilisation du même popup sur les enfants d'un conteneur. Pour activer cette fonctionnalité:

myChild.setInheritsPopupMenu(true);
kleopatra
la source
2
@ user681159 n'en connais pas - et ce n'est pas nécessaire, l'OMI, lisez simplement la doc api :-)
kleopatra
2
Comment utiliseriez-vous cela avec un JTablepour qu'il apparaisse sur la ligne sélectionnée ou sur la ligne où vous faites un clic droit? Ou dans ce scénario, l'ancienne méthode est-elle celle à choisir?
Alex Burdusel
1
@Burfee soit ça, soit améliorez JTable via la sous-classification: remplacez getPopupLocation (..) et stockez l'emplacement pour une utilisation ultérieure, voir un contrôle qualité récent qui est implémenté dans tous les composants de la collection SwingX
kleopatra
18

Il existe une section sur la création d'un menu contextuel dans l'article Comment utiliser les menus des didacticiels Java qui explique comment utiliser la JPopupMenuclasse.

L'exemple de code dans le didacticiel montre comment ajouter des MouseListeners aux composants qui doivent afficher un menu contextuel et affiche le menu en conséquence.

(La méthode que vous décrivez est assez similaire à la façon dont le didacticiel présente la façon d'afficher un menu contextuel sur un composant.)

coobird
la source
8

Le code suivant implémente un menu contextuel par défaut connu à partir des fonctions Windowsde copier, couper, coller, sélectionner tout, annuler et rétablir. Cela fonctionne également sur Linuxet 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);
    }
}

Usage:

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

Maintenant, le textAreaaura un menu contextuel quand il sera cliqué avec le bouton droit de la souris.

BullyWiiPlaza
la source
Excellente solution. Une chose: vous pourriez / devriez utiliser releasedEvent.isPopupTrigger()au lieu de releasedEvent.getButton() == MouseEvent.BUTTON3pour fonctionner correctement sur toutes les plateformes.
Frederic Leitenberger le
Encore un bogue dans le key-listener: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()ceux - ci doivent être les deux Exou non Ex. La Exversion de getMenuShortcutKeyMask()n'est disponible que depuis java 10+.
Frederic Leitenberger le
1

Je corrigerai l'utilisation de cette méthode suggérée par @BullyWillPlaza. La raison en est que lorsque j'essaie d'ajouter ajouter textArea uniquement à contextMenu, ce n'est pas visible, et si je l'ajoute à la fois à contextMenu et à certains panneaux, il compte: une double association parent différente si j'essaie de passer à l'éditeur de conception.

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

Créez un écouteur de souris comme celui-ci pour l'objet texte sur lequel vous devez avoir une fenêtre contextuelle. Ce que cela fera, c'est que lorsque vous cliquez avec le bouton droit sur votre objet texte, il ajoutera cette fenêtre contextuelle et l'affichera. De cette façon, vous ne rencontrez pas cette erreur. La solution créée par @BullyWillPlaza est très bonne, riche et rapide à implémenter dans votre programme, vous devriez donc l'essayer pour voir comment vous l'aimez.

Đumić Branislav
la source
N'oubliez pas non plus que vous devez toujours importer ce contextMenu et créer une nouvelle instance.
Đumić Branislav