Como faço para criar um menu de contexto do botão direito no Java Swing?

110

No momento, estou criando um menu de contexto de clique com o botão direito instanciando um novo JMenuclique com o botão direito e definindo sua localização para a posição do mouse ... Existe uma maneira melhor?

Wayne
fonte

Respostas:

140

Você provavelmente está chamando manualmente setVisible(true)no menu. Isso pode causar um comportamento desagradável de erros no menu.

O show(Component, int x, int x)método lida com todas as coisas que você precisa para acontecer, (destacando coisas ao passar o mouse e fechando o pop-up quando necessário) onde usar setVisible(true)apenas mostra o menu sem adicionar nenhum comportamento adicional.

Para fazer um menu pop-up com o botão direito, basta criar um JPopupMenu.

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

Em seguida, tudo o que você precisa fazer é adicionar um personalizado MouseListeneraos componentes para os quais deseja que o menu seja exibido.

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

Claro, os tutoriais têm uma explicação um pouco mais aprofundada .

Nota: Se você notar que o menu pop-up está aparecendo muito diferente de onde o usuário clicou, tente usar os métodos e.getXOnScreen()e e.getYOnScreen()para as coordenadas xey.

jjnguy
fonte
Depois de usar o código acima, recebo o erro dizendo que "O método addMouseListener (MouseListener) no tipo Figura não é aplicável para os argumentos (PopClickListener)" Atenciosamente, Vinay
1
@ user1035905 Você se certificou de que PopClickListenerestende MouseAdapter?
jjnguy
Como você faz com que funcione com a tecla do menu de contexto no teclado?
Christoffer Hammarström
o único caso em que esta solução é melhor do que a de kleopatra é quando você precisa que alguma lógica personalizada aconteça (por exemplo, menus popup diferentes em condições diferentes); ainda assim, você tem que adicionar ouvinte de teclado para trabalhar com a tecla do menu de contexto
2
o que componentsignifica?
Loint
117

Esta pergunta é um pouco antiga - assim como as respostas (e o tutorial também)

A API atual para definir um popupMenu no Swing é

myComponent.setComponentPopupMenu(myPopupMenu);

Desta forma, será mostrado automagicamente, tanto para gatilhos de mouse quanto de teclado (o último depende do LAF). Além disso, ele suporta a reutilização do mesmo pop-up nos filhos de um contêiner. Para ativar esse recurso:

myChild.setInheritsPopupMenu(true);
Cleópatra
fonte
2
@ user681159 não conheço nenhum - e não é necessário, IMO, simplesmente leia o documento da API :-)
kleopatra
2
Como você usaria isso com um JTablepara que apareça na linha selecionada ou na linha onde você clicar com o botão direito? Ou neste cenário o método antigo é o que deve ser escolhido?
Alex Burdusel
1
@Burfee quer isso ou aprimore JTable via subclasse: sobrescrever getPopupLocation (..) e armazenar o local para uso posterior, veja um QA recente que é implementado em todos os componentes da coleção SwingX
kleopatra
18

Há uma seção sobre como abrir um menu pop-up no artigo How to Use Menus dos Tutoriais Java que explica como usar a JPopupMenuclasse.

O código de exemplo no tutorial mostra como adicionar MouseListeners aos componentes que devem exibir um menu pop-up e exibe o menu de acordo.

(O método que você descreve é ​​bastante semelhante à maneira como o tutorial apresenta a maneira de mostrar um menu pop-up em um componente.)

coobird
fonte
8

O código a seguir implementa um menu de contexto padrão conhecido Windowscom as funções copiar, cortar, colar, selecionar tudo, desfazer e refazer. Também funciona em LinuxeMac 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);

Agora, o textAreaterá um menu de contexto quando for clicado com o botão direito.

BullyWiiPlaza
fonte
Ótima solução. Uma coisa: você pode / deve usar em releasedEvent.isPopupTrigger()vez de releasedEvent.getButton() == MouseEvent.BUTTON3funcionar corretamente em todas as plataformas.
Frederic Leitenberger
Mais um bug no key-listener: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()devem ser ambos Exou não Ex. A Exversão do getMenuShortcutKeyMask()está disponível apenas a partir do java 10+.
Frederic Leitenberger
1

Vou corrigir o uso desse método sugerido por @BullyWillPlaza. A razão é que, quando tento adicionar textArea apenas a contextMenu, ela não fica visível e, se eu adicionar a contextMenu e a alguns painéis, ela econtadores: Associação dupla de pai diferente se eu tentar mudar para o editor de design.

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

Torne o ouvinte do mouse como este para o objeto de texto que você precisa ter ativado. O que isso fará é quando você clicar com o botão direito em seu objeto de texto, ele adicionará esse pop-up e o exibirá. Dessa forma, você não encontrará esse erro. A solução que @BullyWillPlaza fez é muito boa, rica e rápida de implementar em seu programa, então você deve experimentar ou ver se gosta.

Đumić Branislav
fonte
Também não se esqueça de que você ainda precisa importar esse contextMenu e criar uma nova instância.
Đumić Branislav