右键单击JPopupMenu(UIManager问题)时做某事

时间:2016-04-08 08:57:51

标签: java swing listener jpopupmenu uimanager

我正在尝试右键单击JPopupMenu时显示JTable。 我已经以不同的方式实现了这一点,但是没有一个允许我做这个问题:在我右键单击表后执行一些代码,但是弹出菜单在屏幕上。

当显示弹出菜单并在其外部右键单击时,它会在另一个位置再次打开。所以必须有一个内部的听众告诉它这样做,但我无法找到它。理想情况下,我会@Override它并执行我在右键单击JTable时执行的相同代码。

因此,总结一下这个问题,当菜单仍然具有焦点(或正在显示)时,如果我在菜单外部(或者在表格单元格上)右键单击,如何触发某些代码?

编辑:仔细观察后,我发现了问题,但没有找到解决方案。 JPopupMenu不是问题,而是UIManager。以下是我的测试表单的代码,它显示了我描述的问题:

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;

public class PopupTesting extends javax.swing.JFrame {

    public PopupTesting() {
        initComponents();
    }

    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        jTable1.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent evt) {
                jTable1MousePressed(evt);
            }
        });
        jScrollPane1.setViewportView(jTable1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(61, 61, 61)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(83, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(69, 69, 69)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(121, Short.MAX_VALUE))
        );
        pack();
    }                      

    private void jTable1MousePressed(java.awt.event.MouseEvent evt) {                                     
        if (SwingUtilities.isRightMouseButton(evt)) {
            jTable1.setRowSelectionInterval(jTable1.rowAtPoint(evt.getPoint()), jTable1.rowAtPoint(evt.getPoint()));
            getPopup().show(jTable1, evt.getX(), evt.getY());
        }
    }                                    

    public static void main(String args[]) {
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
        }
        java.awt.EventQueue.invokeLater(() -> {
            new PopupTesting().setVisible(true);
        });
    }

    private JPopupMenu getPopup() {
        if (jTable1.getSelectedRow() > -1) {
            JPopupMenu popup = new JPopupMenu();

            MouseListener listener = (new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    System.out.println("I'm a menu Item.");
                }
            });

            JMenuItem m = new JMenuItem("Regular Menu Item");
            m.addMouseListener(listener);
            popup.add(m);

            return popup;
        } return null;
    }

    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;               
}

如果删除该行:

javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());

以及周围的trycatch,它完美无缺。

2 个答案:

答案 0 :(得分:1)

  

或在表格之上

表头不是表的一部分,因此它不会响应鼠标事件。您需要向表头添加一个单独的侦听器。

  

当您点击其他地方并且菜单在屏幕上时,它会关闭并触发您尝试点击的点击事件

如果单击表格的单元格,则在Windows 7上使用JDK8可以正常工作。

如果您仍然遇到问题,请发布一个显示问题的SSCCE

编辑:

正如MadProgrammer在评论中所建议的那样,应该使用setComponentPoupMenu(...)方法。这是一个较新的API。我不知道这两种方法之间有什么区别,但使用这种方法对我来说都适用于我测试过的LAF。

EDIT2:

它再一次对我来说仍然很好。请注意,您不应将MouseListener添加到菜单项。您使用ActionListener来处理菜单项的单击:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TableRightClick extends JFrame implements ActionListener
{
    JPopupMenu popup;

    public TableRightClick()
    {
        popup = new JPopupMenu();
        popup.add( new JMenuItem("Do Something1") );
        JMenuItem menuItem = new JMenuItem("ActionPerformed");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.ALT_MASK));

        menuItem.addActionListener( this );
        popup.add( menuItem );

        JTable table = new JTable(10, 5);

        table.addMouseListener( new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                JTable source = (JTable)e.getSource();
                int row = source.rowAtPoint( e.getPoint() );
                int column = source.columnAtPoint( e.getPoint() );

                if (! source.isRowSelected(row))
                    source.changeSelection(row, column, false, false);
            }
        });

        table.setComponentPopupMenu( popup );

        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        getContentPane().add( new JScrollPane(table) );

        JMenuBar menuBar = new JMenuBar();
        setJMenuBar( menuBar );
        menuBar.add( popup );
    }

    public void actionPerformed(ActionEvent e)
    {
        Component c = (Component)e.getSource();
        JPopupMenu popup = (JPopupMenu)c.getParent();
        JTable table = (JTable)popup.getInvoker();
        System.out.println(table.getSelectedRow() + " : " + table.getSelectedColumn());
    }

    public static void main(String[] args)
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception ex) { System.out.println(ex); }

        TableRightClick frame = new TableRightClick();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}

答案 1 :(得分:1)

好吧,经过几个小时的尝试解决问题,我最终做了一个狡猾的修复。执行我要求的代码在这里:

import java.awt.AWTException;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

public class PopupTesting extends javax.swing.JFrame {

    public PopupTesting() {
        initComponents();
        jTable.setComponentPopupMenu(getPopup());
    }

    volatile int lastMouse = 1;

    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jTable = new javax.swing.JTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        jTable.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent evt) {
                jTableMousePressed(evt);
            }
        });

        jScrollPane1.setViewportView(jTable);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(61, 61, 61)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(83, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(69, 69, 69)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(121, Short.MAX_VALUE))
        );
        pack();
    }

    private void jTableMousePressed(java.awt.event.MouseEvent evt) {                                    
        lastMouse = evt.getButton();
        if (lastMouse == 3) lastMouse--;
        lastMouse = InputEvent.getMaskForButton(lastMouse);
    }                                 


    public static void main(String args[]) {
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
        }
        java.awt.EventQueue.invokeLater(() -> {
            new PopupTesting().setVisible(true);
        });
    }

    private JPopupMenu getPopup() {
        JPopupMenu popup = new JPopupMenu();

        JMenuItem m = new JMenuItem("Regular Menu Item");
        m.addActionListener((ActionEvent e) -> {
            System.out.println("I'm a menu Item.");
        });
        popup.add(m);
        PopupTesting frame = this;
        popup.addAncestorListener( new AncestorListener() {
            @Override
            public void ancestorAdded(AncestorEvent event) {
                Point mousePoint = MouseInfo.getPointerInfo().getLocation();
                mousePoint = SwingUtilities.convertPoint(frame, parseInt(mousePoint.getX() - getX()), parseInt(mousePoint.getY() - getY()), jTable);
                jTable.setRowSelectionInterval(jTable.rowAtPoint(mousePoint), jTable.rowAtPoint(mousePoint));
            }
            @Override public void ancestorRemoved(AncestorEvent event) {
                try {
                    Robot bot = new Robot();
                    bot.mousePress(lastMouse);
                    bot.mouseRelease(lastMouse);
                } catch (AWTException ex) {
                    Logger.getLogger(PopupTesting.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            @Override public void ancestorMoved(AncestorEvent event) {}
        });
        return popup;
    }


    private int parseInt(Double val) {
        return val.intValue();
    }

    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable;               
}

尽管setComponentPopupMenu()在仍然可见时再次显示弹出窗口,但它仍然不会触发MouseListener

我“修复”它的方式是使用AncestorListener,在显示弹出菜单时触发ancestorAdded,并在退出屏幕时触发ancestorRemoved

使用ancestorAdded,我只是这样做,以便每当菜单显示时,它也会选择它背后的单元格(花了很多时间来弄清楚如何从{{1}获得相同的位置我可以从MouseInfo得到,但我到了那里。

MouseEvent,我不得不“欺骗”。基本上,每次我在桌面上点击(ancestorRemoved)时,我都会存储用于执行此操作的按钮。 然后,一旦MousePressed被触发,它就会创建一个ancestorRemoved,重复最后一次鼠标点击(如果是鼠标1则触发鼠标1,鼠标2触发鼠标2等)。

此解决方案有效,但在退出Robot时不允许我按住鼠标按钮。如果有人相信知道解决方案,请告诉我,但现在问题已经解决了。