Làm cách nào để tạo menu ngữ cảnh nhấp chuột phải trong Java Swing?


110

Tôi hiện đang tạo menu ngữ cảnh khi nhấp chuột phải bằng cách tạo một menu mới JMenukhi nhấp chuột phải và đặt vị trí của nó thành vị trí của chuột ... Có cách nào tốt hơn không?

Câu trả lời:


140

Bạn có thể đang gọi theo cách thủ công setVisible(true)trên menu. Điều đó có thể gây ra một số hành vi lỗi khó chịu trong menu.

Các show(Component, int x, int x)xử lý phương pháp tất cả trong những điều bạn cần phải xảy ra, (Nêu bật những thứ trên mouseover và đóng popup khi cần thiết), nơi sử dụngsetVisible(true) chỉ cho thấy menu mà không cần thêm bất kỳ hành vi bổ sung.

Để tạo menu bật lên nhấp chuột phải, chỉ cần tạo một JPopupMenu.

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

Sau đó, tất cả những gì bạn cần làm là thêm một tùy chỉnh MouseListenervào các thành phần mà bạn muốn menu bật lên.

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

Tất nhiên, các hướng dẫn có giải thích sâu hơn một chút .

Lưu ý: Nếu bạn nhận thấy rằng menu bật lên xuất hiện khác với nơi người dùng đã nhấp vào, hãy thử sử dụng e.getXOnScreen()e.getYOnScreen()các phương thức cho tọa độ x và y.


Sau khi sử dụng đoạn mã trên, tôi nhận được báo lỗi nói rằng "Phương pháp addMouseListener (MouseListener) trong các loại hình không áp dụng cho các đối số (PopClickListener)" Kính trọng, Vinay

1
@ user1035905 Bạn có đảm bảo rằng phần PopClickListenermở rộng MouseAdapterkhông?
jjnguy

Làm cách nào để bạn có thể làm cho nó hoạt động với phím menu ngữ cảnh trên bàn phím?
Christoffer Hammarström 23/12/12

trường hợp duy nhất mà giải pháp này tốt hơn kleopatra là khi bạn cần một số logic tùy chỉnh xảy ra (ví dụ: các menu bật lên khác nhau trong các điều kiện khác nhau); vẫn còn, bạn phải thêm người nghe bàn phím để làm việc với phím menu ngữ cảnh

2
viết tắt của cái gì component?
Loint

117

Câu hỏi này hơi cũ - cũng như các câu trả lời (và cả hướng dẫn nữa)

Api hiện tại để thiết lập một popupMenu trong Swing là

myComponent.setComponentPopupMenu(myPopupMenu);

Bằng cách này, nó sẽ được hiển thị tự động, cho cả trình kích hoạt chuột và bàn phím (kích hoạt sau này phụ thuộc vào LAF). Thêm vào đó, nó hỗ trợ sử dụng lại cùng một cửa sổ bật lên trên các phần tử con của vùng chứa. Để bật tính năng đó:

myChild.setInheritsPopupMenu(true);

2
@ user681159 không biết bất kỳ - và nó không cần thiết, IMO, chỉ cần đọc doc api :-)
Kleopatra

2
Làm thế nào bạn sẽ sử dụng điều này với một JTableđể nó xuất hiện trên hàng đã chọn hoặc trên hàng mà bạn nhấp chuột phải? Hay trong kịch bản này, phương pháp cũ là phương pháp được chọn?
Alex Burdusel

1
@Burfee hoặc là hoặc tăng cường JTable qua subclassing: override getPopupLocation (..) và lưu trữ các địa điểm cho việc sử dụng sau, thấy một QA gần đây được thực hiện trong tất cả các thành phần bộ sưu tập SwingX
Kleopatra

18

Có một phần về Đưa menu bật lên trong bài viết Cách sử dụng menu của Hướng dẫn Java giải thích cách sử dụng JPopupMenulớp.

Mã ví dụ trong hướng dẫn cho thấy cách thêm MouseListeners vào các thành phần sẽ hiển thị menu bật lên và hiển thị menu tương ứng.

(Phương pháp bạn mô tả khá giống với cách hướng dẫn trình bày cách hiển thị menu bật lên trên một thành phần.)


8

Đoạn mã sau thực hiện một menu ngữ cảnh mặc định được biết đến Windowsvới các chức năng sao chép, cắt, dán, chọn tất cả, hoàn tác và làm lại. Nó cũng hoạt động trên LinuxMac 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);
    }
}

Sử dụng:

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

Bây giờ textAreasẽ có một menu ngữ cảnh khi nó được nhấp chuột phải vào.


Giải pháp tuyệt vời. Một điều: bạn có thể / nên sử dụng releasedEvent.isPopupTrigger()thay vì releasedEvent.getButton() == MouseEvent.BUTTON3để hoạt động bình thường trên tất cả các nền tảng.
Frederic Leitenberger

Một lỗi nữa trong key-Listening: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()chúng phải là cả hai Exhoặc không Ex. Các Exphiên bản của getMenuShortcutKeyMask()chỉ có sẵn từ java 10+.
Frederic Leitenberger

1

Tôi sẽ sửa cách sử dụng cho phương pháp mà @BullyWillPlaza đã đề xuất. Lý do là khi tôi cố gắng thêm add textArea vào chỉ contextMenu, nó không hiển thị và nếu tôi thêm nó vào cả contextMenu và một số bảng điều khiển, nó sẽ xuất hiện: Liên kết kép chính khác nhau nếu tôi cố gắng chuyển sang trình chỉnh sửa Thiết kế.

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

Tạo trình nghe chuột như thế này cho đối tượng văn bản mà bạn cần bật cửa sổ bật lên. Điều này sẽ làm là khi bạn nhấp chuột phải vào đối tượng văn bản của mình, sau đó nó sẽ thêm cửa sổ bật lên đó và hiển thị nó. Bằng cách này bạn không gặp phải lỗi đó. Giải pháp mà @BullyWillPlaza thực hiện rất tốt, phong phú và nhanh chóng để triển khai trong chương trình của bạn, vì vậy bạn nên thử nó của chúng tôi để xem bạn thích nó như thế nào.


Cũng đừng quên rằng bạn vẫn cần nhập contextMenu đó và tạo phiên bản mới.
Đumić Branislav
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.