Wie erstelle ich ein Kontextmenü mit Rechtsklick in Java Swing?

110

Ich erstelle derzeit ein Kontextmenü mit Rechtsklick, indem ich beim Klicken JMenumit der rechten Maustaste einen neuen Kontextmenü instanziiere und dessen Position auf die Position der Maus einstelle ... Gibt es einen besseren Weg?

Wayne
quelle

Antworten:

140

Sie rufen wahrscheinlich manuell setVisible(true)im Menü auf. Das kann zu bösem Buggy-Verhalten im Menü führen.

Die show(Component, int x, int x)Methode behandelt alle Dinge, die passieren müssen (Hervorheben von Dingen beim Mouseover und Schließen des Popups bei Bedarf), wobei bei Verwendung setVisible(true)nur das Menü angezeigt wird, ohne dass zusätzliches Verhalten hinzugefügt wird.

Um ein Popup-Menü mit der rechten Maustaste zu erstellen, erstellen Sie einfach ein JPopupMenu.

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

Anschließend müssen Sie nur noch eine benutzerdefinierte MouseListenerKomponente zu den Komponenten hinzufügen, für die das Menü angezeigt werden soll.

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

Natürlich haben die Tutorials eine etwas ausführlichere Erklärung.

Hinweis: Wenn Sie feststellen, dass das Popup-Menü weit entfernt von der Stelle angezeigt wird, auf die der Benutzer geklickt hat, verwenden Sie die Methoden e.getXOnScreen()und e.getYOnScreen()für die x- und y-Koordinaten.

jjnguy
quelle
Nachdem ich den obigen Code verwendet habe, erhalte ich die Fehlermeldung, dass "Die Methode addMouseListener (MouseListener) im Typ Figure gilt nicht für die Argumente (PopClickListener)". Grüße, Vinay
1
@ user1035905 Hast du sichergestellt, dass sich das PopClickListenererweitert MouseAdapter?
jjnguy
Wie funktioniert es mit der Kontextmenütaste auf der Tastatur?
Christoffer Hammarström
Der einzige Fall, in dem diese Lösung besser ist als die von Kleopatra, ist, wenn Sie eine benutzerdefinierte Logik benötigen (z. B. verschiedene Popup-Menüs unter verschiedenen Bedingungen).
2
Wofür steht das component?
Loint
117

Diese Frage ist etwas alt - ebenso wie die Antworten (und auch das Tutorial)

Die aktuelle API zum Einstellen eines Popup-Menüs in Swing ist

myComponent.setComponentPopupMenu(myPopupMenu);

Auf diese Weise wird es sowohl für Maus- als auch für Tastaturauslöser automatisch angezeigt (letzteres hängt von LAF ab). Außerdem wird die Wiederverwendung desselben Popups für die untergeordneten Elemente eines Containers unterstützt. So aktivieren Sie diese Funktion:

myChild.setInheritsPopupMenu(true);
Kleopatra
quelle
2
@ User681159 weiß keine - und es wird nicht benötigt, IMO, lesen Sie einfach das API-Dokument :-)
Kleopatra
2
Wie würden Sie dies mit einem verwenden, JTabledamit es in der ausgewählten Zeile oder in der Zeile, in der Sie mit der rechten Maustaste klicken, angezeigt wird? Oder ist in diesem Szenario die alte Methode zu wählen?
Alex Burdusel
1
@Burfee entweder das oder JTable über Unterklasse erweitern: Überschreiben Sie getPopupLocation (..) und speichern Sie den Speicherort für die spätere Verwendung. Eine aktuelle Qualitätssicherung finden Sie in allen SwingX-Auflistungskomponenten
kleopatra
18

Es gibt einen Abschnitt zum Aufrufen eines Popup-Menüs im Artikel Verwenden von Menüs in den Java-Tutorials, in dem die Verwendung der JPopupMenuKlasse erläutert wird .

Der Beispielcode im Lernprogramm zeigt, wie Sie MouseListeners zu den Komponenten hinzufügen , die ein Popup-Menü anzeigen sollen, und zeigt das Menü entsprechend an.

(Die von Ihnen beschriebene Methode ähnelt der Art und Weise, wie im Lernprogramm ein Popup-Menü für eine Komponente angezeigt wird.)

Coobird
quelle
8

Der folgende Code implementiert ein Standardkontextmenü Windowsmit Funktionen zum Kopieren, Ausschneiden, Einfügen, Auswählen aller, Rückgängig machen und Wiederherstellen. Es funktioniert auch auf Linuxund 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);
    }
}

Verwendung:

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

Jetzt textAreawird ein Kontextmenü angezeigt, wenn Sie mit der rechten Maustaste darauf klicken.

BullyWiiPlaza
quelle
Tolle Lösung. Eine Sache: Sie könnten / sollten verwenden, releasedEvent.isPopupTrigger()anstatt releasedEvent.getButton() == MouseEvent.BUTTON3auf allen Plattformen richtig zu arbeiten.
Frederic Leitenberger
Noch ein Fehler im Key-Listener: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()Dies müssen beide sein Exoder nicht Ex. Die ExVersion von getMenuShortcutKeyMask()ist erst seit Java 10+ verfügbar.
Frederic Leitenberger
1

Ich werde die Verwendung für diese von @BullyWillPlaza vorgeschlagene Methode korrigieren. Der Grund dafür ist, dass wenn ich versuche, textArea nur zu contextMenu hinzuzufügen, es nicht sichtbar ist, und wenn ich es sowohl zu contextMenu als auch zu einem Panel hinzufüge, wird Folgendes festgestellt: Unterschiedliche übergeordnete doppelte Zuordnung, wenn ich versuche, zum Design-Editor zu wechseln.

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

Erstellen Sie einen solchen Maus-Listener für Textobjekte, für die ein Popup erforderlich ist. Wenn Sie mit der rechten Maustaste auf Ihr Textobjekt klicken, wird dieses Popup hinzugefügt und angezeigt. Auf diese Weise tritt dieser Fehler nicht auf. Die von @BullyWillPlaza erstellte Lösung ist sehr gut, umfangreich und schnell in Ihrem Programm zu implementieren. Probieren Sie sie aus, um zu sehen, wie es Ihnen gefällt.

Đumić Branislav
quelle
Vergessen Sie auch nicht, dass Sie dieses Kontextmenü noch importieren und eine neue Instanz erstellen müssen.
Đumić Branislav