jTable右键弹出菜单

26

我有一个SQL数据库,并且正在开发一个程序,可以让我添加/删除/修改记录。我已经成功地添加了记录,现在正在编辑/删除它们。

我想在表格中显示现有的记录,所以我正在使用jTable。我在网上找到一些代码并进行了修改,以拉取记录并在jtable中显示它们,但我不知道如何编写右键单击并显示弹出菜单的代码。

在弹出菜单中,我想显示删除记录和修改记录等选项。

这是我正在使用的代码来创建jTable并显示数据:

 private void menuDeleteAuthorActionPerformed(java.awt.event.ActionEvent evt) {                                                 
    TableFromDatabase deleteAuthor = new TableFromDatabase();
    deleteAuthor.pack();
    deleteAuthor.setVisible(true);

    Vector columnNames = new Vector();
    Vector data = new Vector();

    try
    {

        Connection connection = DriverManager.getConnection( url, user, password );

        //  Read data from a table

        String sql = "SELECT * FROM Authors";
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery( sql );
        ResultSetMetaData md = rs.getMetaData();
        int columns = md.getColumnCount();

        //  Get column names

        for (int i = 1; i <= columns; i++)
        {
            columnNames.addElement( md.getColumnName(i) );
        }

        //  Get row data

        while (rs.next())
        {
            Vector row = new Vector(columns);

            for (int i = 1; i <= columns; i++)
            {
                row.addElement( rs.getObject(i) );
            }

            data.addElement( row );
        }

        rs.close();
        stmt.close();
        connection.close();
    }
    catch(Exception e)
    {
        System.out.println( e );
    }

    //  Create table with database data

    JTable table = new JTable(data, columnNames)
    {
        public Class getColumnClass(int column)
        {
            for (int row = 0; row < getRowCount(); row++)
            {
                Object o = getValueAt(row, column);

                if (o != null)
                {
                    return o.getClass();
                }
            }

            return Object.class;
        }
    };

    JScrollPane scrollPane = new JScrollPane( table );
    getContentPane().add( scrollPane );

    JPanel buttonPanel = new JPanel();
    getContentPane().add( buttonPanel, BorderLayout.SOUTH );
}

我是Java的新手,请在回复时友善一些。非常感谢您提供的任何帮助!


请查看JComponent#setComponentPopup - MadProgrammer
这个解决方案(类似的问题尚未在此处链接)对我来说效果最好:https://dev59.com/UWvXa4cB1Zd3GeqPOO7h#17316876 - Joshua Goldberg
4个回答

48

这里有一个例子,说明如何实现此操作。最简单的方法是直接在JTable上设置JPopupMenu

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;

public class TestTableRightClick {

    protected void initUI() {
        final JFrame frame = new JFrame(TestTableRightClick.class.getSimpleName());
        Vector<String> columns = new Vector<String>(Arrays.asList("Name", "Age"));
        Vector<Vector<String>> data = new Vector<Vector<String>>();
        for (int i = 0; i < 50; i++) {
            Vector<String> row = new Vector<String>();
            for (int j = 0; j < columns.size(); j++) {
                row.add("Cell " + (i + 1) + "," + (j + 1));
            }
            data.add(row);
        }
        final JTable table = new JTable(data, columns);
        final JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem deleteItem = new JMenuItem("Delete");
        deleteItem.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "Right-click performed on table and choose DELETE");
            }
        });
        popupMenu.add(deleteItem);
        table.setComponentPopupMenu(popupMenu);
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestTableRightClick().initUI();
            }
        });
    }
}

如果您想要自动选择鼠标右键单击的行,请添加以下代码片段:

popupMenu.addPopupMenuListener(new PopupMenuListener() {

            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        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) {
                // TODO Auto-generated method stub

            }

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

            }
        });

2
使用setComponentPopupMenu()方法加1,这样您就不必担心编写MouseListener了。 - camickr
1
@JoshuaGoldberg 这取决于你想如何处理这个问题。通常,我猜删除操作会在选定的行上进行,插入操作可能会在当前选定行之后进行。右键单击不一定会选择 JTable 的行,这取决于 L&F。如果需要,您可以通过添加 MouseListener 来强制右键单击时更改行选择。这种实现将解决问题。 - Guillaume Polet
1
向量(Vector)是一个已经被弃用的类。 - user1755546
@GuillaumePolet 我尝试了你的建议,使用表格上的MouseListener强制进行行选择。但是这个技巧不起作用,因为setComponentPopupMenu将以某种方式具有最高优先级,并防止MouseListener对任何右键单击做出反应。 - Jan
1
@Jan 请看我在结尾处的更新,其中提供了一段代码片段,可以在右键单击时强制选择该行。 - Guillaume Polet
显示剩余3条评论

18

JTable存在的一个问题是右键单击不会改变行选择。因此,如果您要对特定行起作用的操作,您需要先左键单击该行,然后右键单击以显示弹出菜单。

如果您希望在右键单击时选择行,则可以使用以下代码:

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") );
        popup.add( new JMenuItem("Do Something2") );
        popup.add( new JMenuItem("Do Something3") );
        JMenuItem menuItem = new JMenuItem("ActionPerformed");
        menuItem.addActionListener( this );
        popup.add( menuItem );

        JTable table = new JTable(50, 5);
        table.addMouseListener( new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                System.out.println("pressed");
            }

            public void mouseReleased(MouseEvent e)
            {
                if (e.isPopupTrigger())
                {
                    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);

                    popup.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        getContentPane().add( new JScrollPane(table) );
    }

    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)
    {
        TableRightClick frame = new TableRightClick();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}

0

不幸的是,这些解决方案都无法在我的Mac和PC上运行。不确定原因。但是以下方法可以在两者上都运行:

@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    int row = table.rowAtPoint(SwingUtilities.convertPoint(popup, 0, 0, table));

    if (row > -1)
        table.setRowSelectionInterval(row, row);
}

我的方案的主要问题是,在用户选择了菜单选项之后才显示所选择的行。

2
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

-2
另一个问题是上下文菜单是动态的,您的解决方案无法处理根据点击的行更改菜单的情况。
popupMenu.addPopupMenuListener(new PopupMenuListener() 
{
   @Override
   public void popupMenuWillBecomeVisible(PopupMenuEvent e) 
   { 
       int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
       generateTablePopupMenu(rowAtPoint); // here
       SwingUtilities.invokeLater(new Runnable() 
           ...

你提到了这个问题,但是在这里没有给出任何答案。 - Bu Saeed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接