简体   繁体   English

当JComboBox中的itemStateChanged获得JTable的选定行

[英]Get selected row of JTable when itemStateChanged in JComboBox

I am trying to fetch the current row data while clicking on the combox. 我试图在单击combox时获取当前行数据。 My problem is that if I try to fetch the details on clicking the combobox the data retrieved is wrong. 我的问题是,如果我尝试获取有关单击组合框的详细信息,则检索到的数据是错误的。

This is populating invalid data in the collection. 这是在集合中填充无效数据。 Please follow exact steps mentioned below to replicate. 请按照下面提到的确切步骤进行复制。

Please run the code to replicate the issue as it works only during initial selection but not after. 请运行代码以复制问题,因为该问题仅在初始选择期间有效,而在之后没有。

NOTE: Please CLICK DIRECLTY ONLY ON THE SECOND COLUMN 注意:请仅在第二列上单击脏污

Step 1: Click on Second Column of Row 1
Step 2: Select- Item 1 
Step 3: Click on Second Column of Row 2
Step 4: Select- Item 2
Step 5: Click on Second Column of Row 3
Step 6: Select- Item 3
WORKS Fine till here :)
Step 7 : Click on Second column of Row 1 and do not change an selection leave it as it is (Just click on the combobox twice)
Step 8 : Click on Second column of Row 2, DO NO CHANGES
Step 9 : Click on Second column of Row 3, DO NO CHANGES
Step 10: NOW randomly click on any of the second columns of rows(1,2,3) and see the output datamap. It really wierd why the event is

Below is the sample code: 下面是示例代码:

import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Vector;

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class TestJCombo extends JFrame {

    public TestJCombo() {
        initialize();
    }

    JTable jTable;
    JComboBox comboBox;
    Map<Integer, String> dataMap;

    private void initialize() {
        setSize(300, 300);
        setLayout(new FlowLayout(FlowLayout.LEFT));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTextField field = new JTextField();
        field.setSize(50000, 25);
        field.setText("                                                                                           ");

        jTable = new JTable();

        comboBox = new JComboBox();
        comboBox.setEditable(true);
        comboBox.addItem("item 1");
        comboBox.addItem("item 2");
        comboBox.addItem("item 3");
        comboBox.setEditable(false);

        dataMap = new LinkedHashMap();

        comboBox.addItemListener(new ItemListener() {

            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {

                    Object selected = comboBox.getSelectedItem();

                    int selectedRow = jTable.getSelectedRow();
                    selectedRow = jTable.convertRowIndexToModel(selectedRow);
                    if (selectedRow != -1) {
                        String user = (String) jTable.getValueAt(selectedRow, 0);
                        String data = "Row: " + (selectedRow + 1) + " :::: " + user + " , "
                                + comboBox.getSelectedItem();
                        dataMap.put(selectedRow + 1, "[" + user + " - " + comboBox.getSelectedItem() + "]");
                        if (selected != null) {
                            field.setText(data);
                        }
                        System.out.println("Current data map:: " + dataMap);
                    }
                }

            }
        });

        jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        jTable.setRowHeight(30);
        jTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

        DefaultTableModel myTableMdl = new DefaultTableModel();
        myTableMdl.addColumn("User");
        myTableMdl.addColumn("Role");
        jTable.setModel(myTableMdl);

        jTable.getColumn("Role").setCellEditor(new DefaultCellEditor(comboBox));

        Vector tableData;
        for (int i = 1; i <= 7; i++) {
            tableData = new Vector();
            tableData.add("User " + i);
            myTableMdl.addRow(tableData);
        }

        getContentPane().add(jTable);
        getContentPane().add(field);

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TestJCombo().setVisible(true);
            }
        });
    }
}

The problem is that you are using the wrong listener on the wrong component. 问题是您在错误的组件上使用了错误的侦听器。 You should not be adding a listener to the combo box. 您不应将侦听器添加到组合框。 The point of using an editor is to edit the data and update the TableModel . 使用编辑器的目的是编辑数据并更新TableModel You should not be referencing the combo box for further processing. 您不应引用组合框进行进一步处理。

So instead you should be adding a TableModelListener to the TableModel . 因此,您应该向TableModelListener添加一个TableModel Then a TableModelEvent will be generated whenever the value in column 2 is changed. 然后,只要更改第2列中的值,就会生成TableModelEvent The TableModelEvent will contain the row/column information of the cell that was changed. TableModelEvent将包含已更改的单元格的行/列信息。

You can check out: JTable -> TableModeListener for a basic example of using a TableModelListener. 您可以签出: JTable-> TableModeListener以获取使用TableModelListener的基本示例。 In your case you check if the second column changed and then you update your map. 在您的情况下,请检查第二列是否已更改,然后更新地图。

Also note you are using the convertRowIndexToModel() method incorrectly: 还要注意,您错误地使用了convertRowIndexToModel()方法:

selectedRow = jTable.convertRowIndexToModel(selectedRow);
...
    String user = (String) jTable.getValueAt(selectedRow, 0);

First of all you only need to worry about coverting the row index if the table view is sorted or filtered. 首先,如果对表视图进行排序或过滤,则只需要担心覆盖行索引。 In the code provided you don't do either so there is no need to convert the index. 在提供的代码中,您也不执行任何操作,因此无需转换索引。

However, if you ever did sort of filter the view then you would need convert the view row to the model row and then you must access the data from the model, not the view. 但是,如果您曾经对视图进行过某种筛选,则需要将视图行转换为模型行,然后必须从模型而不是视图访问数据。 So the code would be: 因此,代码将是:

String user = (String) jTable.getModel().getValueAt(selectedRow, 0);

The problem is that you are creating one JComboBox and then putting it on all the cells in the column. 问题是您要创建一个JComboBox ,然后将其放在列中的所有单元格上。 You are referencing the JComboBox , not creating a new one for each cell. 您正在引用JComboBox ,而不是为每个单元格创建一个新的。 This is why when you follow the 10 steps you mentioned, the result is obtained is wildly foolish and senseless. 这就是为什么当您按照提到的10个步骤进行操作时,获得的结果非常愚蠢和毫无意义。

Here is the fix, create a custom TableCellEditor for all the cells that are in column 1 (Role column). 这是解决方法,为列1(“角色”列)中的所有单元格创建一个自定义TableCellEditor The goal is to create a brand new JComboBox for each cell. 目标是为每个单元创建一个全新的JComboBox This is achieved by changing the declaration of the JTable to jtable = new JTable(){...}; 这可以通过将JTable的声明更改为jtable = new JTable(){...}; to override the getCellEditor(...){...} method. 重写getCellEditor(...){...}方法。 Also, the method private JComboBox createComboBox() {...} is what you need for brand new JComboBox creation. 另外, private JComboBox createComboBox() {...}是全新JComboBox创建所需要的。

Now, we will take out the addItemListener and replace it with addActionListener . 现在,我们将取出addItemListener并将其替换为addActionListener This is needed because the itemStateChanged in ItemListener will be not invoked if the user selects the item that is already selected. 这是必需的,因为如果用户选择已选择的项目,则不会调用ItemListeneritemStateChanged We need to somewhat replicate a two-mouse-click, one for showing the dropdown list and the second for selecting the item (you can skip this step if not needed). 我们需要略微复制两次鼠标单击,一次单击显示下拉列表,第二次单击选择项目(如果不需要,可以跳过此步骤)。

I have also edited your code to make it more readable and efficient . 我还编辑了您的代码,以使其更具可读性和效率。

Here is an MVCE: 这是MVCE:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.table.TableCellEditor;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Vector;
import javax.swing.Box;
import javax.swing.BoxLayout;

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class TestJCombo extends JFrame {

    JTable jTable;
    //JComboBox comboBox;//not needed, replaced by the createComboBox() method.
    Map<Integer, String> dataMap;
    final JTextField FIELD = new JTextField(25);//must put this globally.
    //Since it is final, it should be all in upper case

    //It is a good practice to put the global variables on top and constructor(s) below.
    public TestJCombo() {
        initialize();
    }

    private void initialize() {
        //use pack(); instead setSize(...); I used it at the end of this method.
        //setSize(300, 300);
        setLayout(new FlowLayout(FlowLayout.LEFT));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        //both lines not needed, it has been taken care of when FIELD was declared.
        //FIELD.setSize(50000, 25);
        //FIELD.setText("                                         "
        //  + "                                                  ");

        //Must create a seperate TableCellEditor for each cell in the table.
        jTable = new JTable() {
            public TableCellEditor getCellEditor(int row, int column) {
                int modelColumn = convertColumnIndexToModel(column);
                //if the cell lies in the second column, create a custom cell editor.
                if (modelColumn == 1) {
                    return (TableCellEditor) new DefaultCellEditor(createComboBox());
                } else {
                    return super.getCellEditor(row, column);
                }
            }
        };

        //comboBox = new JComboBox();
        //comboBox.setEditable(true);
        //comboBox.addItem("item 1");
        //comboBox.addItem("item 2");
        //comboBox.addItem("item 3");
        //comboBox.setEditable(false);
        dataMap = new LinkedHashMap();

        jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        jTable.setRowHeight(30);
        jTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

        DefaultTableModel myTableMdl = new DefaultTableModel();
        myTableMdl.addColumn("User");
        myTableMdl.addColumn("Role");
        jTable.setModel(myTableMdl);

        //we have our own custom CellEditor, this is not needed
        //jTable.getColumn("Role").setCellEditor(new DefaultCellEditor(comboBox));
        Vector tableData;
        for (int i = 1; i <= 7; i++) {
            tableData = new Vector();
            tableData.add("User " + i);
            myTableMdl.addRow(tableData);
        }

        //It is better practice to add everything to a 
        //panel and then add that panel to the frame.
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        panel.add(jTable);
        panel.add(Box.createRigidArea(new Dimension(0, 10)));//add some space
        panel.add(FIELD);
        getContentPane().add(panel, BorderLayout.CENTER);
        pack();//should use this.
    }

    private JComboBox createComboBox() {
        JComboBox comboBox = new JComboBox();
        comboBox.setEditable(true);
        comboBox.addItem("item 1");
        comboBox.addItem("item 2");
        comboBox.addItem("item 3");
        comboBox.setEditable(false);

        //Add an ActionListener so that it would also detect if the user selects
        //the same item. The itemStateChanged in the ItemListener will not be
        //invoked if the user selects the item that is already selected.
        comboBox.addActionListener(new ActionListener() {
            //To update the result everytime the user selects an item
            //(regardless if it was selected or not), we need to "mock"
            //a two-click operation. The first click will show the 
            //drop-down list to select from and the second will
            //allow the user to select the desired choice to be selected.
            boolean doubleClick = false;

            @Override
            public void actionPerformed(ActionEvent ae) {
                if (doubleClick) {
                    int selectedRow = jTable.getSelectedRow();
                    selectedRow = jTable.convertRowIndexToModel(selectedRow);
                    Object selected = comboBox.getSelectedItem();
                    if (selectedRow != -1 && selected != null) {
                        String user = (String) jTable.getValueAt(selectedRow, 0);
                        String data = "Row: " + (selectedRow + 1) + " :::: " + user + " , "
                            + comboBox.getSelectedItem();
                        dataMap.put(selectedRow + 1, "[" + user + " - "
                            + comboBox.getSelectedItem() + "]");
                        FIELD.setText(data);
                        System.out.println("Current data map:: " + dataMap);
                    }
                    doubleClick = false;
                }
                doubleClick = true;
            }
        });

//      comboBox.addItemListener(new ItemListener() {
//
//          public void itemStateChanged(ItemEvent e) {
//              if (e.getStateChange() == ItemEvent.SELECTED) {
//
//                  Object selected = comboBox.getSelectedItem();
//
//                  int selectedRow = jTable.getSelectedRow();
//                  selectedRow = jTable.convertRowIndexToModel(selectedRow);
//                  if (selectedRow != -1) {
//                      String user = (String) jTable.getValueAt(selectedRow, 0);
//                      String data = "Row: " + (selectedRow + 1) + " :::: " + user + " , "
//                          + comboBox.getSelectedItem();
//                      dataMap.put(selectedRow + 1, "[" + user + " - "
//                      + comboBox.getSelectedItem() + "]");
//                      if (selected != null) {
//                          FIELD.setText(data);
//                      }
//                      System.out.println("Current data map:: " + dataMap);
//                  }
//              }
//
//          }
//      });
        return comboBox;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TestJCombo().setVisible(true);
            }
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM