Как создать контекстное меню правой кнопки мыши в Java Swing?

110

В настоящее время я создаю контекстное меню, вызываемое JMenuщелчком правой кнопкой мыши, создавая новый экземпляр при щелчке правой кнопкой мыши и устанавливая его местоположение в соответствии с положением мыши ... Есть ли лучший способ?

Wayne
источник

Ответы:

141

Вы, вероятно, вручную вызываете setVisible(true)меню. Это может вызвать некоторые неприятные ошибки в меню.

Этот show(Component, int x, int x)метод обрабатывает все, что вам нужно (выделение элементов при наведении курсора мыши и закрытие всплывающего окна при необходимости), при использовании которого setVisible(true)просто отображается меню без добавления какого-либо дополнительного поведения.

Чтобы создать всплывающее меню при щелчке правой кнопкой мыши, просто создайте файл JPopupMenu.

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

Затем все, что вам нужно сделать, это добавить пользовательские настройки MouseListenerк компонентам, для которых вы хотите отображать всплывающее меню.

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

Конечно, в руководствах есть более подробное объяснение.

Примечание: Если вы заметили , что всплывающее меню появляется поодаль от того, где пользователя нажал, попробуйте использовать e.getXOnScreen()и e.getYOnScreen()методы для координат х и у.

jjnguy
источник
После использования приведенного выше кода я получаю сообщение об ошибке: «Метод addMouseListener (MouseListener) в типе Figure неприменим для аргументов (PopClickListener)» С уважением, Виней
1
@ user1035905 Вы убедились, что расширение PopClickListenerрасширяется MouseAdapter?
jjnguy 08
Как заставить его работать с клавишей контекстного меню на клавиатуре?
Christoffer Hammarström
единственный случай, когда это решение лучше, чем у kleopatra, - это когда вам нужна некоторая настраиваемая логика (например, разные всплывающие меню в разных условиях); тем не менее, вам нужно добавить прослушиватель клавиатуры для работы с клавишей контекстного меню
2
что означает component?
Loint
117

Этот вопрос немного устарел - как и ответы (и само руководство)

Текущий api для настройки popupMenu в Swing:

myComponent.setComponentPopupMenu(myPopupMenu);

Таким образом, он будет отображаться автоматически, как для триггеров мыши, так и для клавиатуры (последнее зависит от LAF). Кроме того, он поддерживает повторное использование одного и того же всплывающего окна для дочерних элементов контейнера. Чтобы включить эту функцию:

myChild.setInheritsPopupMenu(true);
Kleopatra
источник
2
@ user681159 ничего не знаю - и это не нужно, ИМО, просто прочтите api doc :-)
kleopatra
2
Как бы вы использовали это с, JTableчтобы он появлялся в выбранной строке или в строке, где вы щелкаете правой кнопкой мыши? Или в этом сценарии следует выбрать старый метод?
Alex Burdusel
1
@Burfee либо это, либо улучшите JTable с помощью подкласса: переопределите getPopupLocation (..) и сохраните местоположение для последующего использования, см. Недавний QA, который реализован во всех компонентах коллекции
SwingX
18

В статье « Как использовать меню» учебников по Java есть раздел « Вызов всплывающего меню», в котором объясняется, как использовать этот класс.JPopupMenu

В примере кода в руководстве показано, как добавить MouseListeners к компонентам, которые должны отображать всплывающее меню и соответственно отображать меню.

(Метод, который вы описываете, довольно похож на способ, которым в учебнике представлен способ отображения всплывающего меню в компоненте.)

Coobird
источник
8

Следующий код реализует контекстное меню по умолчанию, известное из Windowsфункций копирования, вырезания, вставки, выбора всего, отмены и повтора. Он также работает Linuxи 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);
    }
}

Использование:

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

Теперь при textAreaщелчке правой кнопкой мыши у него будет контекстное меню.

BullyWiiPlaza
источник
Отличное решение. Одно: вы могли / должны использовать releasedEvent.isPopupTrigger()вместо того, releasedEvent.getButton() == MouseEvent.BUTTON3чтобы правильно работать на всех платформах.
Фредерик Лейтенбергер,
Еще одна ошибка в ключевом прослушивателе: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()либо оба, либо Exнет Ex. ExВерсия getMenuShortcutKeyMask()доступна только с Java 10+.
Фредерик Лейтенбергер,
1

Я исправлю использование этого метода, предложенного @BullyWillPlaza. Причина в том, что когда я пытаюсь добавить textArea только в contextMenu, он не отображается, и если я добавляю его как в contextMenu, так и в какую-то панель, он обнаруживает: Другая родительская двойная ассоциация, если я пытаюсь переключиться в редактор дизайна.

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

Сделайте такой слушатель мыши для текстового объекта, который вам нужен. Это произойдет, если вы щелкнете правой кнопкой мыши по текстовому объекту, и тогда всплывающее окно добавится и отобразится. Таким образом, вы не столкнетесь с этой ошибкой. Решение, созданное @BullyWillPlaza, очень хорошее, богатое и быстрое для реализации в вашей программе, поэтому вы должны попробовать его, чтобы увидеть, как оно вам нравится.

Думич Бранислав
источник
Также не забывайте, что вам все еще нужно импортировать это contextMenu и создать новый экземпляр.
umić Branislav