JTable неправильно возвращает выбранную строку

Я работаю с расширением DefaultTableModel следующим образом:

Это NEW AchievementTableModel после обновления, чтобы отразить ввод некоторых ответов.

public AchievementTableModel(Object[][] c, Object[] co) {
    super(c,co);
}
public boolean isCellEditable(int r, int c) {return false;}
public void replace(Object[][] c, Object[] co) {
    setDataVector(convertToVector(c), convertToVector(co));
    fireTableDataChanged();
}

Мой графический интерфейс представляет собой JTable со следующими свойствами:

if(table==null)
    table = new JTable(model);
else
    table.setModel(model);
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().setColumnSelectionAllowed(false);

У меня есть JComboBox, который выбирает, какие данные отображать. TableModel обновляется вызовом model.replace(cells), а затем снова запускает приведенный выше код создания таблицы.

При выборе строки в GUI JTable и печати значения table.getSelectedRow() я ВСЕГДА получаю -1 после изменения данных таблицы с помощью вызова model.replace(cells) из первого выбора, даже если я повторно выбираю первый JComboBox вариант. Есть ли причина для этого, что мне не хватает? Должен ли я изменить какой-то код?

РЕДАКТИРОВАТЬ: Код сильно изменился, пытаясь ответить на этот вопрос, поэтому вот обновленный код. Новая модель AchievementTableModel приведена выше.

Это настраивает модель и таблицу для правильного просмотра и отображения в ScrollPane.

if(model==null)
    model = new AchievementTableModel(cells, columns);
else
    model.replace(cells, columns);
if(table==null) {
    table = new JTable(model);
    table.setFillsViewportHeight(true);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getTableHeader().setReorderingAllowed(false);
    table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
    table.getColumnModel().setColumnSelectionAllowed(false);
    table.getTableHeader().setResizingAllowed(false);
} else
    table.setModel(model);

column = table.getColumn(columns[0]);
column.setPreferredWidth(25);
column = table.getColumn(columns[1]);
column.setPreferredWidth(225);
column = table.getColumn(columns[2]);
column.setPreferredWidth(40);
table.doLayout();

add(new JScrollPane(table), BorderLayout.CENTER);

person Kevin Stich    schedule 25.06.2009    source источник
comment
можете ли вы показать метод, в котором вы печатаете table.getSelectedRow(), пожалуйста   -  person akf    schedule 26.06.2009
comment
Есть кнопка с прикрепленным к ней действием, которое использует метод getSelectedRow() и печатает выбранную строку в System.out. Я выбираю строку и нажимаю кнопку, но всегда получаю вывод -1.   -  person Kevin Stich    schedule 26.06.2009
comment
MMMhh Вам не нужно переопределять все эти методы. Вы повторяете то, что уже делает суперкласс, и, возможно, теряете часть слушателя в процессе. Прокомментируйте все эти методы и сделайте то, что предложил Клинт. Давай посмотрим что происходит.   -  person OscarRyz    schedule 26.06.2009
comment
Я удалил все дополнительные методы переопределения, которые не были нужны, так что остались только isCellEditable и replace. Затем переделал конструктор на super(c,co); и это ничего не изменило.   -  person Kevin Stich    schedule 26.06.2009
comment
В чем причина вызова table = new JTable, а не table.setModel(model)?   -  person Tom Neyland    schedule 26.06.2009
comment
Это было изменено, обновив источник в вопросе   -  person Kevin Stich    schedule 26.06.2009
comment
Не опускайте { в одиночных операторах if/else НИКОГДА!!!! не ленись, когда-нибудь оно укусит тебя очень глубоко и больно... bit.ly/16pOwS   -  person OscarRyz    schedule 26.06.2009


Ответы (8)


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

Второе изменение:

вместо этого:

  if(model==null)
    model = new AchievementTableModel(cells, columns);
  else
    model.replace(cells, columns);
  if(table==null) {
    table = new JTable(model);
    table.setFillsViewportHeight(true);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getTableHeader().setReorderingAllowed(false);
    table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
    table.getColumnModel().setColumnSelectionAllowed(false);
    table.getTableHeader().setResizingAllowed(false);
  } else
    table.setModel(model);

  column = table.getColumn(columns[0]);
  column.setPreferredWidth(25);
  column = table.getColumn(columns[1]);
  column.setPreferredWidth(225);
  column = table.getColumn(columns[2]);
  column.setPreferredWidth(40);
  table.doLayout();

  add(new JScrollPane(table), BorderLayout.CENTER);

сделайте это вместо этого:

 if(model==null) {
    model = new AchievementTableModel(cells, columns);
 } else {
    model.setDataVector(cells, columns);
 }
 if(table==null) {
    table = new JTable(model);
    table.setFillsViewportHeight(true);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getTableHeader().setReorderingAllowed(false);
    table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
    table.getColumnModel().setColumnSelectionAllowed(false);
    table.getTableHeader().setResizingAllowed(false);

    column = table.getColumn(columns[0]);
    column.setPreferredWidth(25);
    column = table.getColumn(columns[1]);
    column.setPreferredWidth(225);
    column = table.getColumn(columns[2]);
    column.setPreferredWidth(40);
    table.doLayout();

    add(new JScrollPane(table), BorderLayout.CENTER);
   } else {
    table.setModel(model);
   }

вам не нужно добавлять таблицу в новую область прокрутки и повторно добавлять ее на панель при каждом изменении модели.

person akf    schedule 25.06.2009
comment
Если я уберу это воссоздание JTable, таблица станет пустой, когда я переключу модель. Что я должен использовать, чтобы таблица снова появилась? - person Kevin Stich; 26.06.2009
comment
Столбцы также исчезают. - person Kevin Stich; 26.06.2009
comment
это звучит так, как будто вы также очищаете свою модель стола. чтобы это заработало, вы можете создать новую модель таблицы, и вместо создания новой JTable вы можете вызвать setModel() для таблицы. - person akf; 26.06.2009
comment
Я установил JTable в setModel вместо нового JTable после нулевой проверки и вместо этого создал новую модель. Это не решило проблему, все равно все пропадает. - person Kevin Stich; 26.06.2009
comment
вы все еще вызываете replace()? Вы создаете новую AchievementTableModel каждый раз, когда меняете комбо? - person akf; 26.06.2009
comment
Создание новой модели с новыми данными вместо вызова замены. - person Kevin Stich; 26.06.2009
comment
Я скопировал настройки макета вместо того, чтобы перемещать их, и все в порядке. Спасибо за вашу помощь! - person Kevin Stich; 26.06.2009

Хорошо, теперь мне интересно

Похоже, вам действительно нужно почистить свой код, потому что вокруг много ссылок.

Причина, по которой вы не видите таблицу с выбранным индексом, заключается в том, что каждый раз, когда вы создаете новый JTable, метод, в котором вы печатаете выбранную запись, по-прежнему указывает на оригинал. Поскольку теперь вы показываете «вновь» созданную таблицу, старая печатает -1.

Причина, по которой вы получаете пустую таблицу при использовании DefaultTableModel, заключается в том, что векторы являются null (возможно, полученными из комбо), и, таким образом, и данные, и заголовки исчезают из таблицы.

Вам не нужен подкласс, если вы все равно используете Object[][] в качестве данных.

Итак, вот более простой тестовый класс, который вы можете увидеть, чтобы исправить свой.

Я тестирую его как с вашим пользовательским TableModel, так и с DefaultTableModel

Это не имеет ничего общего с вашей пользовательской моделью таблицы, а с тем, как вы используете свои ссылки.

Надеюсь, это поможет.

import javax.swing.*;
import java.awt.*;
import javax.swing.table.*;
import java.util.*;
import java.awt.event.*;
public class Test { 

    private DefaultTableModel tableModel = null;
    //private AchievementTableModel tableModel = null;
    private Object []   headers = new Object[]{"Name", "Last Name"};
    private Object [][] data;
    private Object [][] dataA = new Object[][]{{"Oscar","Reyes"},{"John","Doe"}};
    private Object [][] dataB = new Object[][]{{"Color","Green"},{"Thing","Car"}};
    private JTable table;


    public static void main( String [] args ) { 
        Test test = new Test();
        test.main();
    }
    public void main() { 
        // Create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

        // Create the unique table.
        table = new JTable();
        frame.add(new JScrollPane( table ));

        // Add two buttons
        frame.add( new JPanel(){{ 
            // swap table model button ( simulates combo )
            add(new JButton("Change Table model"){{
                addActionListener( new ActionListener() { 
                    public void actionPerformed( ActionEvent e ) { 
                        if( tableModel == null ) { 
                            data = dataA;
                            tableModel = new DefaultTableModel( data, headers );
                            //tableModel = new AchievementTableModel( data, headers );
                            table.setModel( tableModel );
                        } else { 
                            data = data == dataA ? dataB : dataA;
                            tableModel.setDataVector( data, headers );
                            //tableModel.replace( data ); // not needed DefaultTableModel already has it.

                        }
                    }
                });
            }});
            // and print selectedRow button
            add( new JButton("Print selected row"){{
                addActionListener( new ActionListener() { 
                    public void actionPerformed( ActionEvent e ) { 
                        System.out.println(table.getSelectedRow());
                    }
                });
            }});

        }}, BorderLayout.SOUTH);

        // show the frame
        frame.pack();
        frame.setVisible( true );
    }

}

Ваш подкласс не изменился.

class AchievementTableModel extends DefaultTableModel {

    public AchievementTableModel(Object[][] c, Object[] co) {
        super.dataVector = super.convertToVector(c);
        super.columnIdentifiers = super.convertToVector(co);
    }
    public int getColumnCount() {return super.columnIdentifiers.size();}
    public int getRowCount() {return super.dataVector.size();}
    public String getColumnName(int c) {return (String)super.columnIdentifiers.get(c);}
    @SuppressWarnings("unchecked")
    public Object getValueAt(int r, int c) {return ((Vector<Object>)super.dataVector.get(r)).get(c);}
    public boolean isCellEditable(int r, int c) {return false;}
    public void replace(Object[][] c) {
        super.dataVector = super.convertToVector(c);
        super.fireTableDataChanged();
    }
}

Попробуйте и посмотрите, как он не теряет ссылку на таблицу и всегда печатает правильные selectedRow.

альтернативный текст

Сравните его с вашим кодом и исправьте его оттуда.

person OscarRyz    schedule 25.06.2009
comment
Я собираюсь опубликовать свой обновленный код, потому что в процессе решения этой проблемы многое изменилось. После этого я посмотрю, какие изменения я могу сделать из вашего примера. - person Kevin Stich; 26.06.2009
comment
Да. Немного очистите его (удалив конфиденциальную информацию) и опубликуйте. Держу пари, это очень легко исправить. - person OscarRyz; 26.06.2009
comment
Код был обновлен в исходном сообщении, пожалуйста, посмотрите. Спасибо за вашу помощь. - person Kevin Stich; 26.06.2009

Может быть, попробуйте использовать

super.setDataVector (вектор данных, векторные имена столбцов);

javax.​swing.​table.​DefaultTableModel
public void setDataVector(Vector dataVector, Vector columnIdentifiers)

Из JavaDoc

Заменяет текущую переменную экземпляра dataVector новым вектором строк, dataVector. Каждая строка представлена ​​в dataVector как вектор значений объекта. columnIdentifiers — это имена новых столбцов. Первое имя в columnIdentifiers сопоставляется со столбцом 0 в dataVector. Каждая строка в dataVector настраивается в соответствии с количеством столбцов в columnIdentifiers либо путем усечения вектора, если он слишком длинный, либо путем добавления нулевых значений, если он слишком короткий. Обратите внимание, что передача значения null для dataVector приводит к неуказанному поведению, возможно, к исключению. Параметры: dataVector - новый вектор данных columnIdentifiers - имена столбцов

person Tom Neyland    schedule 25.06.2009
comment
Я попытался изменить метод замены на это: public void replace(Object[][] c, Object[] co) { setDataVector(convertToVector(c), convertToVector(co)); FireTableDataChanged(); } и это не решило проблему. - person Kevin Stich; 26.06.2009
comment
Когда вы вызываете convertToVector, он преобразует его в вектор векторов? или вектор массивов? - person Tom Neyland; 26.06.2009
comment
Для каждого есть по одному, предназначенному для имен данных и столбцов соответственно. - person Kevin Stich; 26.06.2009

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

Я укажу, что по моему опыту, именно поэтому я склонен расширять AbstractTableModel или правильно реализовывать свой собственный TableModel интерфейс с нуля. Изменение ссылки на резервные данные, как здесь, всегда вызывает миллион проблем ИМХО.

person Petriborg    schedule 25.06.2009
comment
Я выбираю снова после того, как данные заменены вызовом replace(cells), поэтому я не думаю, что это проблема. - person Kevin Stich; 26.06.2009
comment
Вы снова выбираете их как пользователя после вызова replace(cells), и он по-прежнему возвращает индекс -1? - person Petriborg; 26.06.2009
comment
Он возвращает правильный индекс только до того, как я вызову замену, но после всегда будет возвращать -1, даже после выбора множества разных строк в JTable. - person Kevin Stich; 26.06.2009
comment
Изменение ссылки на резервные данные, как здесь, всегда вызывает миллион проблем ИМХО. +1, я тоже - person Tom Neyland; 26.06.2009
comment
Я бы сделал это, но похоже, что при переходе от AbstractTableModel к DefaultTableModel много работы за кулисами. Не могли бы вы сказать мне, какие части должны быть изменены и / или как? Это было бы большим подспорьем. - person Kevin Stich; 26.06.2009
comment
Я думаю, это зависит от того, сколько этой функциональности вам нужно, конечно! В 90% случаев мне нужна только возможность addRow() и иногда moveRow(). Я признаю, что если вам нужно иметь возможность добавлять столбцы, а также строки, это становится более сложным. - person Petriborg; 26.06.2009

Когда вы переупорядочиваете JTable, вам нужно отслеживать исходные индексы в TableModel для ваших данных, а не текущие индексы в JTable. Визуально таблица могла сместиться, но базовая модель данных — нет.

person Gandalf    schedule 25.06.2009
comment
Если я не ошибаюсь, TableModel изменяется, когда я изменяю все ячейки данных, вызывается fireTableDataChanged(), и я создаю новую JTable с обновленной моделью. - person Kevin Stich; 26.06.2009
comment
вы создаете новый JTable или просто сбрасываете модель? - person akf; 26.06.2009
comment
Я вызываю model.replace(cells) и сразу после вызова table = new JTable(model) - person Kevin Stich; 26.06.2009
comment
См. Ниже, я не хочу такого поведения. - person Kevin Stich; 26.06.2009

Похоже, при изменении ваш выбор теряется.

Возвращает ли "getSelectedRow()" что-либо "ДО" изменения модели?

Если это так, то сохраните этот индекс, измените модель и снова установите этот индекс.

Вероятно, вам нужно использовать пользовательскую ListSelectionModel. для этого

person OscarRyz    schedule 25.06.2009
comment
Он возвращает правильную информацию до изменения модели, но я не хочу, чтобы поведение чего-то выбиралось на основе того, что было выбрано ранее. Когда модель изменена, я хочу, чтобы пользователь выбрал новую строку. В моем тестировании выбор строки после обновления модели и создания новой таблицы всегда будет давать -1 независимо от данных или выбора. - person Kevin Stich; 26.06.2009
comment
О, я понял!!.. Кажется, что слушатель продолжает смотреть на старую модель стола, а не на новую. Попробуйте осмотреться вокруг этого. Осмотрите ListSelectionListener и посмотрите, что или где это указывает. Кроме того, не обращайтесь к super.dataVector напрямую, для этого должен быть аксессуар. - person OscarRyz; 26.06.2009
comment
Когда вы запрашиваете индекс, вы запрашиваете текущую таблицу или случайно старую таблицу? - person akarnokd; 26.06.2009
comment
Я сохраняю только одну ссылку, так что это должна быть текущая таблица. - person Kevin Stich; 26.06.2009

Еще одна вещь, о которой я подумал, когда вы делаете table= new JTable(model);, вы изменяете таблицу, на которую ссылается переменная «таблица», однако это может не вызвать автоматическое отображение новой таблицы.

Если ваша таблица содержится в ScrollPane, вам может потребоваться вызвать ScrollPane.setViewportView(table);

person Tom Neyland    schedule 25.06.2009
comment
Я попытался добавить строку кода, которую вы предложили, но это не повлияло на ситуацию. - person Kevin Stich; 26.06.2009

У меня была такая же проблема с получением -1 всегда для getSelectedRow(). Проблема уже могла быть решена. Тем не менее, размещение кода, который устранил мою проблему:

final int selectedRowIndex = table.rowAtPoint(mouseEvent.getPoint());
final int modelRowIndex = table.convertRowIndexToModel(selectedRowIndex);
person user1639485    schedule 01.10.2012