ฉันจะสร้างเมนูบริบทคลิกขวาใน Java Swing ได้อย่างไร


110

ฉันกำลังสร้างเมนูบริบทคลิกขวาโดยสร้างอินสแตนซ์ใหม่JMenuเมื่อคลิกขวาและตั้งค่าตำแหน่งเป็นตำแหน่งของเมาส์ ... มีวิธีที่ดีกว่านี้ไหม

คำตอบ:


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()วิธีการสำหรับพิกัด x และ y


หลังจากใช้โค้ดด้านบนฉันได้รับข้อผิดพลาดที่แจ้งว่า "วิธีการ addMouseListener (MouseListener) ในประเภท Figure ใช้ไม่ได้กับอาร์กิวเมนต์ (PopClickListener)" ขอแสดงความนับถือ Vinay

1
@ user1035905 คุณแน่ใจหรือไม่ว่าการPopClickListenerขยายนั้น MouseAdapter
jjnguy

คุณจะทำให้มันทำงานกับปุ่มเมนูบริบทบนแป้นพิมพ์ได้อย่างไร?
Christoffer Hammarström

กรณีเดียวที่โซลูชันนี้ดีกว่าของคลีโอพัตราคือเมื่อคุณต้องการตรรกะที่กำหนดเองบางอย่างเพื่อให้เกิดขึ้น (เช่นเมนูป๊อปอัปที่แตกต่างกันภายใต้เงื่อนไขที่แตกต่างกัน) ถึง

2
สิ่งที่ไม่componentยืน?
Loint

117

คำถามนี้ค่อนข้างเก่า - เช่นเดียวกับคำตอบ (และแบบฝึกหัดด้วย)

api ปัจจุบันสำหรับการตั้งค่า popupMenu ใน Swing คือ

myComponent.setComponentPopupMenu(myPopupMenu);

วิธีนี้จะแสดงโดยอัตโนมัติทั้งสำหรับทริกเกอร์เมาส์และคีย์บอร์ด (ขึ้นอยู่กับ LAF) นอกจากนี้ยังรองรับการใช้ป๊อปอัปเดียวกันซ้ำกับลูก ๆ ของคอนเทนเนอร์อีกด้วย ในการเปิดใช้งานคุณสมบัตินั้น:

myChild.setInheritsPopupMenu(true);

2
@ user681159 ไม่รู้ - และไม่จำเป็น IMO เพียงแค่อ่าน api doc :-)
kleopatra

2
คุณจะใช้สิ่งนี้อย่างไรJTableจึงจะปรากฏบนแถวที่เลือกหรือบนแถวที่คุณคลิกขวา หรือในสถานการณ์นี้จะต้องเลือกวิธีการแบบเก่า?
Alex Burdusel

1
@Burfee นั้นหรือปรับปรุง JTable ผ่านคลาสย่อย: แทนที่ getPopupLocation (.. ) และจัดเก็บตำแหน่งสำหรับการใช้งานในภายหลังดู QA ล่าสุดซึ่งนำไปใช้ในส่วนประกอบคอลเลกชัน SwingX ทั้งหมด
kleopatra

18

มีส่วนในการเรียกใช้เมนูป๊อปอัพในบทความวิธีใช้เมนูของThe Java Tutorialsซึ่งอธิบายวิธีใช้JPopupMenuคลาส

โค้ดตัวอย่างในบทช่วยสอนจะแสดงวิธีการเพิ่มMouseListeners ลงในส่วนประกอบซึ่งควรแสดงเมนูป๊อปอัปและแสดงเมนูตามนั้น

(วิธีการที่คุณอธิบายค่อนข้างคล้ายกับวิธีที่บทช่วยสอนนำเสนอวิธีการแสดงเมนูป๊อปอัปบนส่วนประกอบ)


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จะมีเมนูบริบทเมื่อคลิกขวา


ทางออกที่ดี สิ่งหนึ่ง: คุณสามารถ / ควรใช้releasedEvent.isPopupTrigger()แทนreleasedEvent.getButton() == MouseEvent.BUTTON3การทำงานอย่างถูกต้องบนทุกแพลตฟอร์ม
Frederic Leitenberger

ข้อบกพร่องอีกประการหนึ่งในตัวฟังคีย์: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()สิ่งเหล่านี้ต้องเป็นทั้งสองอย่างExหรือไม่Exก็ได้ Exรุ่นgetMenuShortcutKeyMask()จะใช้ได้เฉพาะตั้งแต่ Java 10+
Frederic Leitenberger

1

ฉันจะแก้ไขการใช้งานสำหรับวิธีการนั้นที่ @BullyWillPlaza แนะนำ เหตุผลก็คือเมื่อฉันพยายามเพิ่ม add 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.