仅当我按下键盘上的菜单键时,JPopupMenu 才会跳几行

JPopupMenu jumps few rows only when I press Menu key on keyboard

这很奇怪。当我使用鼠标右键单击时,JPopMenu 按预期工作。但是当我尝试使用键盘上的菜单键显示它时,突出显示会在前后跳几行。

我使用了 PopupMenuListener 以便在使用右键单击时可以直接突出显示一行:

popupMenu.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        Point point = SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table);
                        int currentRow = table.rowAtPoint(point);
                        int currentColumn = table.columnAtPoint(point);
                        table.changeSelection(currentRow, currentColumn, false, false);
                    }
                });
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }
        });

完整程序如下:

import java.awt.Point;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;

import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class Test {

    private JFrame frame;
    private JTable table;
    private JScrollPane scrollpane;

    public Test() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        String[] head = {"ID","NAME","DOB"};
        
        String[][] data = {
                {"1", "Peter", "2001/03/24"},
                {"2", "Carlos", "1996/09/02"},
                {"3", "Ahmed", "1999/07/07"},
                {"4", "John", "1993/10/15"},
                {"5", "Kumar", "1991/11/08"}
        };
        
        table = new JTable(data, head);
        scrollpane = new JScrollPane(table);
        table.setDefaultEditor(Object.class, null);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        
        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem edit = new JMenuItem("Edit");
        JMenuItem delete = new JMenuItem("Delete");
        popupMenu.add(edit);
        popupMenu.add(delete);
        table.setComponentPopupMenu(popupMenu);
        
        popupMenu.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        Point point = SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table);
                        int currentRow = table.rowAtPoint(point);
                        int currentColumn = table.columnAtPoint(point);
                        table.changeSelection(currentRow, currentColumn, false, false);
                    }
                });
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }
        });
        
        frame.getContentPane().add(scrollpane);
    }
    
    public void setVisible(boolean state) {
        frame.setVisible(state);
    }

}

the selection jumps few records after or before the selected row

我更进一步,向 table 添加了 20 行,然后缩小框架以便显示滚动条。

弹出窗口始终显示在视口中间。

这作为默认行为是有意义的,因为弹出窗口可以显示在任何组件上。弹出窗口不知道视口中有具有选择逻辑的组件。

以下代码尝试根据是使用鼠标还是键盘显示弹出窗口来自定义 popup/selection 行为:

import java.awt.*;
import java.awt.event.*;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;

import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

public class Test {

    private JFrame frame;
    private JTable table;
    private JScrollPane scrollpane;

    public Test() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        String[] head = {"ID","NAME","DOB"};

        String[][] data = {
                {"1", "Peter", "2001/03/24"},
                {"2", "Carlos", "1996/09/02"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"3", "Ahmed", "1999/07/07"},
                {"4", "John", "1993/10/15"},
                {"5", "Kumar", "1991/11/08"}
        };

        table = new JTable(data, head);
        scrollpane = new JScrollPane(table);
        table.setDefaultEditor(Object.class, null);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem edit = new JMenuItem("Edit");
        JMenuItem delete = new JMenuItem("Delete");
        popupMenu.add(edit);
        popupMenu.add(delete);
        table.setComponentPopupMenu(popupMenu);

        popupMenu.addPopupMenuListener(new PopupMenuListener()
        {
            private boolean isMouse;

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e)
            {
                isMouse = EventQueue.getCurrentEvent().getID() == MouseEvent.MOUSE_RELEASED;

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run()
                    {
                        if (isMouse) // change row/cell selection
                        {
                            Point point = SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table);
                            int currentRow = table.rowAtPoint(point);
                            int currentColumn = table.columnAtPoint(point);
                            table.changeSelection(currentRow, currentColumn, false, false);
                        }
                        else  // use current selection
                        {
                            int selectedRow = table.getSelectedRow();
                            int selectedColumn = table.getSelectedColumn();

                            if (selectedRow == -1) // no row selected
                            {
                                popupMenu.setVisible( false );
                            }
                            else  // reset popup location to selected row
                            {
                                Rectangle bounds = table.getCellRect(selectedRow, selectedColumn, false);
                                popupMenu.show(table, bounds.x, bounds.y + bounds.height);
                            }
                        }
                    }
                });
            }

            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }

            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
                // TODO Auto-generated method stub
            }
        });

        frame.getContentPane().add(scrollpane);
    }

    public void setVisible(boolean state) {
        frame.setVisible(state);
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> new Test().setVisible(true) );
    }
}

当使用鼠标时,选择将被更改,弹出窗口将显示在鼠标位置。

使用键盘时,弹出位置基于当前选定的单元格。