在 JTable 中检测鼠标单击时出现问题

Trouble detecting mouse click in JTable

我有一个 JTable,其中第 3 列和第 4 列使用 JComboBox 作为单元格编辑器。第 3 列指定类型,第 4 列指定子类型。第 4 列中可用的子类型列表取决于用户为该类型选择的值。此外,类型和子类型列表由用户编辑table(细节在这里并不重要,只是类型和子类型列表都可以在运行时更改)。

因为子类型组合框的内容是动态的,我的目的是检测用户何时点击进入子类型单元格,并在此时根据内容设置列表模型(设置可用的子类型项目列表)该行中的类型。

但是,我无法检测到所需单元格中的鼠标单击。我在下面发布了我的代码和输出。

问题是鼠标侦听器未检测到组合框中的单击。据推测,该事件是由组合框触发的,而不是 table。我可以将 ListSelectionListener 与 TableColumnModelListener 结合使用,但我不得不想象有更好的方法。我尝试使用与组合框相关的事件,但我无法获得有关选择了哪个组合框的信息(所以我不知道我在 table 中的什么位置)。我试图获取组合框的容器并以这种方式定位点击,但是组合框的 getParent() 方法 returns null.

编辑:在 ComboBox ActionListener 中,我还尝试使用 JTable 的 getSelectedRow() 和 getSelectedColumn() 方法,但它们 return 旧单元格的值,而不是新单元格的值。

如有任何关于如何更简洁地执行此操作的建议,我们将不胜感激。

以下是相关的代码片段(事件处理程序还没有做太多,只是试图获取信息):

    // Setup table and table model
    tableTypeDataModel = new TypeAssignmentTableModel();
    JTable typeTable = new JTable(tableTypeDataModel);
    // Add a mouse listener to the table. 
    typeTable.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e)
        {   
           Point pnt = e.getPoint();
           int row = typeTable.rowAtPoint(pnt);
           int col = typeTable.columnAtPoint(pnt);
           System.out.println(Integer.toString(row) + ", " + Integer.toString(col));
        }       
    });

    typeTable.getSelectionModel().addListSelectionListener(new myListSelectionListener());
    typeTable.setFillsViewportHeight(true);
    JScrollPane scpnTypeTable = new JScrollPane(typeTable);
    
    // Set columns and model listener
    TableColumnModel cm = typeTable.getColumnModel();
    cm.addColumnModelListener(new myColumnListSelectionListener());
    
   // Setup type column
    JComboBox<String> cmbTableTypes = new JComboBox<>(arrOfTypes);
    cmbTableTypes.addActionListener(new myCBOActionListener());
    cm.getColumn(TYPECOL_TYPE).setCellEditor(new DefaultCellEditor(cmbTableTypes));
    // This just adds left padding to the cell
    IndentedTableCellRenderer tableTypeRenderer = new IndentedTableCellRenderer(5, 0);
    cm.getColumn(TYPECOL_TYPE).setCellRenderer(tableTypeRenderer);
     
    // Setup subtype column
    tableSubtypeCboModel = new DefaultComboBoxModel<>();
    JComboBox<String> cmbTableSubtypes = new JComboBox<>(tableSubtypeCboModel);
    cm.getColumn(TYPECOL_SUBTYPE).setCellEditor(new DefaultCellEditor(cmbTableSubtypes));
    IndentedTableCellRenderer tableSubtypeRenderer = new IndentedTableCellRenderer(5, 0);
    cm.getColumn(TYPECOL_SUBTYPE).setCellRenderer(tableSubtypeRenderer);
     
    

还有...

    class myListSelectionListener implements ListSelectionListener {
        @Override
        public void valueChanged(ListSelectionEvent e) {
            System.out.println("Table List selection listener = " + e.getSource().toString());
        }
    }   

    class myColumnListSelectionListener implements TableColumnModelListener {

        // other methods empty and not shown...

        @Override
        public void columnSelectionChanged(ListSelectionEvent e) {
        // TODO Auto-generated method stub
            System.out.println("Table column model listener Source = " + 
                e.getSource().toString());
        }
    }

    class myCBOActionListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JComboBox item = (JComboBox) e.getSource();
            System.out.println("CBO Action Listener = " + item.getClass().toString());
            String msg = item.getParent() == null ? "null" : item.getParent().toString();
            System.out.println("CBO Action Listener item parent is " + msg);
        }
    }

这是单击以下单元格后的控制台输出:

  Row 1, Col 0 (just a regular cell)
  Row 2, Col 1 (just a regular cell)
  Row 3, Col 2 (just a regular cell)
  Row 4, Col 3 (this is the Type column with a combo box editor)

Console Output:
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{0}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{1}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={1}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={0}
1, 0
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{1}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{2}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={2}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={1}
2, 1
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{2}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{3}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={3}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={2}
3, 2
CBO Action Listener = class javax.swing.JComboBox
CBO Action Listener item parent is null
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{3}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{4}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={4}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={3}

谢谢。

您可以将您的子类型 JCombobox 作为全局管理,然后添加一个 ActionListener,当在 [=16] 中“选择”子类型 JCombobox 组件时更新子类型值=].

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;

public class whatev{

    JComboBox types = new JComboBox();
    JComboBox subtypes = new JComboBox();
    
    Map<String,int[]> m = new HashMap<String,int[]>();  //example source mapping types to sub-types
    int[] t1 = {1,3};
    int[] t2 = {4, 5};
    
    public static void main(String[] args){ new whatev(); }
    
    whatev(){
        //Add example data to types
        types.addItem("Type 1");
        types.addItem("Type 2");
        types.setSelectedIndex(0);
        
        //init map data for example
        m.put("Type 1", t1);
        m.put("Type 2", t2);

        //initialize subtype JCombobox
        updateSubtypes();
        
        //add appropriate listener to subtype box
        subtypes.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateSubtypes();
            }
        });
    }

    private void updateSubtypes() {
        int[] x = m.get(types.getSelectedItem());   //pull appropriate subtypes from type/subtype mapping source
        subtypes.removeAllItems();                  //purge old objects in subtype
        for(int v : x) { subtypes.addItem(v); }     //Add appropriate subtypes  
        }
}

@camickr - 谢谢你的回答。我按照建议覆盖了 prepareEditor 方法,该方法根据需要更新组合框中的 ListModel。

    JTable typeTable = new JTable() {
        @Override
        public Component prepareEditor(TableCellEditor editor, int row, int column) {
            
            if (column == SUBTYPE_COLUMN) {
                // User has entered a subtype cell
                // Get value of the adjacent type cell
                updateCBOSubtypeDataModel(myTableModel.getValueAt(row, TYPE_COLUMN);
            }
            return super.prepareEditor(editor, row, column);
        }
    };

private void updateCBOSubtypeDataModel(TypeObject newType) {
    myComboBoxListModel.removeAllElements();
    myComboBoxListModel.addAll(getListOfSubtypesForThisType(newType));
}

工作顺利。谢谢