Come si crea un menu contestuale del tasto destro in Java Swing?


110

Attualmente sto creando un menu contestuale per JMenuil clic destro istanziando un nuovo clic destro e impostando la sua posizione su quella della posizione del mouse ... C'è un modo migliore?

Risposte:


140

Probabilmente stai chiamando manualmente setVisible(true)dal menu. Ciò può causare un brutto comportamento di bug nel menu.

Il show(Component, int x, int x)metodo gestisce tutte le cose che devi accadere, (evidenziando le cose al passaggio del mouse e chiudendo il popup quando necessario) dove l'uso setVisible(true)mostra solo il menu senza aggiungere alcun comportamento aggiuntivo.

Per creare un menu popup con clic con il pulsante destro del mouse, creare semplicemente un file JPopupMenu.

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

Quindi, tutto ciò che devi fare è aggiungere un custom MouseListenerai componenti per i quali desideri venga visualizzato il menu.

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

Naturalmente, i tutorial hanno una spiegazione leggermente più approfondita .

Nota: se noti che il menu a comparsa appare lontano dal punto in cui l'utente ha fatto clic, prova a utilizzare i metodi e.getXOnScreen()e e.getYOnScreen()per le coordinate x e y.


Dopo aver utilizzato il codice sopra, ottengo l'errore che dice che "Il metodo addMouseListener (MouseListener) nel tipo Figure non è applicabile per gli argomenti (PopClickListener)" Saluti, Vinay

1
@ user1035905 Ti sei assicurato che si PopClickListenerestenda MouseAdapter?
jjnguy

Come fai a farlo funzionare con il tasto del menu contestuale sulla tastiera?
Christoffer Hammarström

l'unico caso in cui questa soluzione è migliore di quella di kleopatra è quando hai bisogno di una logica personalizzata (es. menu a comparsa diversi in condizioni diverse); tuttavia, devi aggiungere l'ascoltatore della tastiera per lavorare con il tasto del menu contestuale

2
cosa significa component?
Loint

117

Questa domanda è un po 'vecchia, così come le risposte (e anche il tutorial)

L'API corrente per l'impostazione di un menu popup in Swing è

myComponent.setComponentPopupMenu(myPopupMenu);

In questo modo verrà mostrato automagicamente, sia per i trigger da mouse che da tastiera (quest'ultima dipende da LAF). Inoltre, supporta il riutilizzo dello stesso popup nei figli di un contenitore. Per abilitare quella funzione:

myChild.setInheritsPopupMenu(true);

2
@ user681159 non ne conosco - e non è necessario, IMO, leggi semplicemente il documento api :-)
kleopatra

2
Come lo useresti con un JTablecosì si apre sulla riga selezionata o sulla riga in cui fai clic con il pulsante destro del mouse? Oppure in questo scenario il vecchio metodo è quello da scegliere?
Alex Burdusel

1
@ Burfee o migliora JTable tramite sottoclassi: sovrascrivi getPopupLocation (..) e memorizza la posizione per un utilizzo successivo, vedi un recente QA che è implementato in tutti i componenti della raccolta SwingX
kleopatra

18

C'è una sezione sulla visualizzazione di un menu a comparsa nell'articolo Come usare i menu di The Java Tutorials che spiega come usare la JPopupMenuclasse.

Il codice di esempio nel tutorial mostra come aggiungere messaggi MouseListenerdi posta elettronica ai componenti che dovrebbero visualizzare un menu a comparsa e visualizza il menu di conseguenza.

(Il metodo che descrivi è abbastanza simile al modo in cui il tutorial presenta il modo di mostrare un menu a comparsa su un componente.)


8

Il codice seguente implementa un menu contestuale predefinito noto da Windowscon funzioni di copia, taglia, incolla, seleziona tutto, annulla e ripeti. Funziona anche su Linuxe 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);

Ora textAreaavrà un menu contestuale quando si fa clic con il pulsante destro del mouse.


Ottima soluzione. Una cosa: potresti / dovresti usare releasedEvent.isPopupTrigger()invece di releasedEvent.getButton() == MouseEvent.BUTTON3funzionare correttamente su tutte le piattaforme.
Frederic Leitenberger

Un altro bug nel key-listener: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()questi devono essere entrambi Exo no Ex. La Exversione di getMenuShortcutKeyMask()è disponibile solo da java 10+.
Frederic Leitenberger,

1

Correggerò l'utilizzo di quel metodo suggerito da @BullyWillPlaza. Il motivo è che quando provo ad aggiungere add textArea solo a contextMenu non è visibile, e se lo aggiungo sia a contextMenu che a qualche pannello ecounters: Diversa doppia associazione genitore se provo a passare all'editor di progettazione.

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

Crea un ascoltatore del mouse in questo modo per l'oggetto di testo su cui devi avere un popup. Ciò che farà è quando fai clic con il pulsante destro del mouse sul tuo oggetto di testo, aggiungerà quel popup e lo visualizzerà. In questo modo non incontri quell'errore. La soluzione creata da @BullyWillPlaza è molto buona, ricca e veloce da implementare nel tuo programma, quindi dovresti provarla per vedere come ti piace.


Inoltre, non dimenticare che devi ancora importare quel contextMenu e creare una nuova istanza.
Đumić Branislav
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.