如果在可见的 JPopupMenu 外部单击,则强制选择 JTable 行

Force JTable row selection if clicking outside visible JPopupMenu

我已将 JPopupMenu 添加到 JTablesetComponentPopupMenu。问题是,虽然 JPopupMenu 是 open/visible,当我左键单击弹出菜单外的一行时,菜单关闭但未选择该行,所以我必须再次单击它以突出显示它。有什么办法可以解决这个问题吗?

编辑

我添加了示例代码。

顺便说一句,这种行为只发生在 Windows LaF 中。我刚刚对其进行了测试,默认的 Java LaF 似乎允许在 JPopupMenu 打开时左键单击行选择。

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class TableSTACK {

    private static void createAndShowGUI() {

        String[] headers = {"Column 1", "Column 2"};

        Object[][] data = { {"Row",   "1"}, {"Row",   "2"},
                {"Row",   "3"}, {"Row",   "4"}, {"Row",   "5"},
                {"Row",   "6"}, {"Row",   "7"}, {"Row",   "8"}, };

        JTable table = new JTable(data, headers);
        table.setFillsViewportHeight(true);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());        

        final JMenuItem item1 = new JMenuItem();
        item1.setText("Menu Item 1");       
        final JMenuItem item2 = new JMenuItem();
        item2.setText("Menu Item 2");
        final JMenuItem item3 = new JMenuItem();
        item3.setText("Menu Item 3");

        final JPopupMenu popupMenu = new JPopupMenu();
        popupMenu.add(item1);
        popupMenu.addSeparator();
        popupMenu.add(item2);
        popupMenu.add(item3);
        table.setComponentPopupMenu(popupMenu);

        popupMenu.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        // force selection of row upon right-click (it works)
                        int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
                        if (rowAtPoint > -1) {
                            table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
                        }
                    }
                });
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        // force row selection upon exiting popup menu
                        // does not work; rowAtPoint always returns -1
                        int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(null, new Point(0, 0), table));
                        //int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
                        if (rowAtPoint > -1) {
                            table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
                        }
                    }
                });
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                // TODO
            }
        });


        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.gridx = 0;

        JScrollPane scrollPane = new JScrollPane(table);

        JPanel contentPane = new JPanel();
        contentPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
        contentPane.setLayout(new GridBagLayout());
        contentPane.add(scrollPane, gbc);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setMinimumSize(new Dimension(500, 400));
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (UnsupportedLookAndFeelException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                createAndShowGUI();
            }
        });
    }
}

这是一个 LAF 问题。

当我使用默认 LAF 时它对我有用,但当我使用平台 LAF 时它不起作用,对我来说是 Windows。

Windows 上的一个潜在解决方案是使用 MouseListener 到 select 线路。请注意,代码已添加到 mouseReleased 事件中。由于某种原因 table 没有收到 mousePressed 事件,即使根据 AWTEventListener table 是 mousePressed 事件的来源。

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

public class TablePopupListener extends JPanel
{
    public TablePopupListener()
    {
        JTable table = new JTable(10, 5);
        add( new JScrollPane( table ) );

        JPopupMenu popup = new JPopupMenu();
        popup.add( new JMenuItem("Do Something1") );
        popup.add( new JMenuItem("Do Something2") );

        table.setComponentPopupMenu( popup );

        table.addMouseListener( new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                System.out.println("Pressed JTable");
            }

            public void mouseReleased(MouseEvent e)
            {
                System.out.println("Released JTable");

                int row = table.rowAtPoint( e.getPoint() );

                if (row != -1
                && !table.isRowSelected(row))
                {
                   table.setRowSelectionInterval(row, row);
                }
            }
        });
    }

    private static void createAndShowGUI()
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception ex) { System.out.println(ex); }

        JFrame frame = new JFrame("TablePopupListener");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new TablePopupListener());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );

        Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
        {
            public void eventDispatched(AWTEvent e)
            {
                String event = null;

                switch (e.getID())
                {
                    case MouseEvent.MOUSE_PRESSED:  event = "Pressed: " ; break;
                    case MouseEvent.MOUSE_RELEASED:  event = "Released: " ; break;
                    case MouseEvent.MOUSE_ENTERED:  event = "Entered: " ; break;
                    case MouseEvent.MOUSE_EXITED:  event = "Exited: " ; break;
                    default: event = null; break;
                }

                if (event != null)
                {
                    System.out.println();
                    System.out.println(event + e.getSource().getClass());
                }
            }
        },  AWTEvent.MOUSE_EVENT_MASK);
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}