Bagaimana cara membuat menu konteks klik kanan di Java Swing?

110

Saat ini saya membuat menu konteks klik kanan dengan membuat instance baru JMenudi klik kanan dan menyetel lokasinya ke posisi mouse ... Adakah cara yang lebih baik?

Wayne
sumber

Jawaban:

140

Anda mungkin memanggil setVisible(true)menu secara manual . Itu dapat menyebabkan beberapa perilaku buggy yang buruk di menu.

The show(Component, int x, int x)Metode menangani semua hal yang Anda perlu terjadi, (Menyoroti hal-hal di mouseover dan menutup popup bila perlu) di mana menggunakansetVisible(true) hanya menunjukkan menu tanpa menambahkan perilaku tambahan.

Untuk membuat menu popup klik kanan, cukup buat file JPopupMenu.

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

Kemudian, yang perlu Anda lakukan adalah menambahkan kustom MouseListenerke komponen yang Anda inginkan untuk menu pop-up.

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

Tentu saja tutorialnya memiliki penjelasan yang sedikit lebih mendalam .

Catatan: Jika Anda melihat bahwa menu popup muncul jauh dari tempat pengguna mengklik, coba gunakan metode e.getXOnScreen()dan e.getYOnScreen()untuk koordinat x dan y.

jjnguy
sumber
Setelah menggunakan kode di atas, saya mendapatkan kesalahan yang mengatakan bahwa "Metode addMouseListener (MouseListener) di tipe Gambar tidak berlaku untuk argumen (PopClickListener)" Salam, Vinay
1
@ user1035905 Apakah Anda memastikan bahwa PopClickListenermeluas MouseAdapter?
jjnguy
Bagaimana Anda membuatnya bekerja dengan tombol menu konteks pada keyboard?
Christoffer Hammarström
satu-satunya kasus di mana solusi ini lebih baik daripada solusi kleopatra adalah ketika Anda memerlukan beberapa logika khusus agar terjadi (mis. menu popup berbeda dalam kondisi berbeda); tetap saja, Anda harus menambahkan pendengar keyboard untuk bekerja dengan tombol menu konteks
2
apa artinya component?
Loint
117

Pertanyaan ini agak tua - begitu pula jawabannya (dan juga tutorialnya)

Api saat ini untuk menyetel popupMenu di Swing adalah

myComponent.setComponentPopupMenu(myPopupMenu);

Dengan cara ini akan ditampilkan secara otomatis, baik untuk pemicu mouse dan keyboard (yang terakhir bergantung pada LAF). Plus, ini mendukung penggunaan kembali popup yang sama di seluruh anak wadah. Untuk mengaktifkan fitur itu:

myChild.setInheritsPopupMenu(true);
kleopatra.dll
sumber
2
@ user681159 tidak tahu apa-apa - dan itu tidak diperlukan, IMO, cukup baca dokumen api :-)
kleopatra
2
Bagaimana Anda akan menggunakan ini dengan JTablesehingga muncul di baris yang dipilih atau di baris di mana Anda klik kanan? Atau dalam skenario ini, metode lama yang akan dipilih?
Alex Burdusel
1
@Burfee baik itu atau tingkatkan JTable melalui subclassing: timpa getPopupLocation (..) dan simpan lokasi untuk penggunaan nanti, lihat QA terbaru yang diterapkan di semua komponen koleksi SwingX
kleopatra
18

Ada bagian tentang Memunculkan Menu Popup di artikel Cara Menggunakan Menu dari Tutorial Java yang menjelaskan cara menggunakanJPopupMenu kelas.

Kode contoh dalam tutorial menunjukkan cara menambahkan MouseListener s ke komponen yang seharusnya menampilkan menu pop-up, dan menayangkan menu yang sesuai.

(Metode yang Anda gambarkan cukup mirip dengan cara tutorial menyajikan cara menampilkan menu pop-up pada suatu komponen.)

burung coobird
sumber
8

Kode berikut mengimplementasikan menu konteks default yang dikenal Windowsdengan fungsi salin, potong, tempel, pilih semua, batalkan, dan ulangi. Ini juga berfungsi pada Linuxdan 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);
    }
}

Pemakaian:

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

Sekarang textAreaakan memiliki menu konteks saat diklik kanan.

BullyWiiPlaza
sumber
Solusi bagus. Satu hal: Anda bisa / harus menggunakan releasedEvent.isPopupTrigger()alih-alih releasedEvent.getButton() == MouseEvent.BUTTON3berfungsi dengan baik di semua platform.
Frederic Leitenberger
Satu bug lagi di key-listener: pressedEvent.getModifiersEx() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()harus ada keduanya Exatau tidak Ex. The Exversi getMenuShortcutKeyMask()ini hanya tersedia sejak java 10 +.
Frederic Leitenberger
1

Saya akan mengoreksi penggunaan untuk metode yang disarankan @BullyWillPlaza. Alasannya adalah ketika saya mencoba menambahkan textArea ke hanya contextMenu itu tidak terlihat, dan jika saya menambahkannya ke kedua contextMenu dan beberapa panel itu ecounters: Asosiasi ganda induk yang berbeda jika saya mencoba untuk beralih ke editor Desain.

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

Jadikan pendengar mouse seperti ini untuk objek teks yang perlu Anda aktifkan popupnya. Apa yang akan dilakukan adalah ketika Anda mengklik kanan pada objek teks Anda, ia akan menambahkan popup itu dan menampilkannya. Dengan cara ini Anda tidak menemukan kesalahan itu. Solusi yang dibuat oleh @BullyWillPlaza sangat bagus, kaya, dan cepat diimplementasikan di program Anda sehingga Anda harus mencobanya, lihat seberapa suka Anda.

Đumić Branislav
sumber
Juga jangan lupa Anda masih perlu mengimpor contextMenu itu dan membuat instance baru.
Đumić Branislav