Получить выбранную строку JTable, когда itemStateChanged в JComboBox

Я пытаюсь получить данные текущей строки, нажимая на поле со списком. Моя проблема в том, что если я попытаюсь получить подробную информацию, щелкнув поле со списком, полученные данные будут неправильными.

Это заполняет коллекцию недопустимыми данными. Пожалуйста, следуйте точным инструкциям, указанным ниже, для воспроизведения.

Запустите код, чтобы воспроизвести проблему, поскольку она работает только во время первоначального выбора, но не после нее.

ПРИМЕЧАНИЕ: НАЖМИТЕ ТОЛЬКО НА ВТОРОЙ КОЛОНКЕ.

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

Ниже приведен пример кода:

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);
            }
        });
    }
}

Метод JTable.getValueAt принимает индексы просмотра, а не индексы модели. Непосредственно перед использованием этого метода вы конвертируете индекс представления в индекс модели и используете в этом вызове индекс модели.

TT. 10.08.2018 11:58
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
1 183
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что вы создаете один JComboBox, а затем помещаете его во все ячейки столбца. Вы ссылаетесь на JComboBox, а не создаете новый для каждой ячейки. Вот почему, когда вы следуете упомянутому вами 10 шагов, получается дико глупый и бессмысленный результат.

Вот исправление, создайте собственный TableCellEditor для всех ячеек, которые находятся в столбце 1 (столбец роли). Цель состоит в том, чтобы создать новый JComboBox для каждой ячейки. Это достигается путем изменения объявления JTable на jtable = new JTable(){...};, чтобы переопределить метод getCellEditor(...){...}. Кроме того, метод private JComboBox createComboBox() {...} - это то, что вам нужно для создания нового JComboBox.

Теперь мы вынимаем addItemListener и заменяем его на addActionListener. Это необходимо, потому что itemStateChanged в ItemListener не будет вызываться, если пользователь выберет уже выбранный элемент. Нам нужно в некоторой степени воспроизвести два щелчка мыши, один для отображения раскрывающегося списка, а второй для выбора элемента (вы можете пропустить этот шаг, если не требуется).

Я также отредактировал ваш код, чтобы сделать его более читабельным и эффективным.

Вот 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);
            }
        });
    }
}

Это решение, наконец, РАБОТАЕТ. Я реализовал ваше предложение, и оно сработало отлично. Помогла и добавленная проверка выполнения двойного действия ... Спасибо

Anonymous 11.08.2018 05:57

(1-) хотя это может сработать, это не способ решения проблемы. ActionListner поля со списком используется JTable для обновления TableModel при выборе значения. Если вы хотите знать, когда данные изменяются, используйте TableModelListener для реализации вашей собственной логики.

camickr 11.08.2018 16:22
Ответ принят как подходящий

Проблема в том, что вы используете неправильный слушатель для неправильного компонента. Вы не должны добавлять слушателя в поле со списком. Смысл использования редактора - редактировать данные и обновлять TableModel. Вы не должны ссылаться на поле со списком для дальнейшей обработки.

Поэтому вместо этого вы должны добавить TableModelListener к TableModel. Тогда TableModelEvent будет генерироваться всякий раз, когда значение в столбце 2 изменяется. TableModelEvent будет содержать информацию о строке / столбце измененной ячейки.

Вы можете проверить: JTable -> TableModeListener для базового примера использования TableModelListener. В вашем случае вы проверяете, изменился ли второй столбец, а затем обновляете карту.

Также обратите внимание, что вы неправильно используете метод convertRowIndexToModel ():

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

Прежде всего, вам нужно только позаботиться о покрытии индекса строки, если представление таблицы отсортировано или отфильтровано. В предоставленном коде вы этого не делаете, поэтому нет необходимости преобразовывать индекс.

Однако, если вы когда-либо отфильтровывали представление, вам нужно было бы преобразовать строку представления в строку модели, а затем вы должны получить доступ к данным из модели, а не к представлению. Итак, код будет:

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

Как было предложено, я удалил код selectedRow = jTable.convertRowIndexToModel (selectedRow); Спасибо .. Однако решение, данное @M. Аль Джумали отлично работает с добавленной проверкой двойного действия - -

Anonymous 11.08.2018 05:56

@Anonymous, the solution given by Jumaily works perfectly - это не вопрос работы, это вопрос использования правильного класса для работы. Вам ни в коем случае не нужно расширять JTable, чтобы получить желаемую функциональность. Вы не используете «молоток» для каждой работы по дому. Вы используете подходящий инструмент. Тот факт, что кто-то пишет код для вас, не означает, что вы должны использовать этот код. Научитесь эффективно использовать Java, используя классы так, как они предназначены для использования. В любом случае не забудьте «принять» ответ, нажав на галочку, чтобы люди знали, что проблема решена.

camickr 11.08.2018 16:18

TableModelListener - хороший подход, и он также отлично работает. Спасибо

Anonymous 16.08.2018 07:05

Другие вопросы по теме