JTable не возвращает выбранную строку правильно - PullRequest
1 голос
/ 26 июня 2009

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

Это НОВАЯ 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 (ячейки), а затем снова проходит через код создания таблицы выше.

При выборе строки в графическом интерфейсе JTable и печати значения table.getSelectedRow () я ВСЕГДА получаю -1 после изменения данных таблицы при вызове model.replace (ячейки) из первого выбора, даже если я выбрал повторный выбор первый вариант 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);

Ответы [ 8 ]

2 голосов
/ 26 июня 2009

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

2-е редактирование:

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

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

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

2 голосов
/ 26 июня 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.

alt text

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

1 голос
/ 26 июня 2009

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

super.setDataVector (Vector dataVector, Vector ColumnNames);

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

Из JavaDoc

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

1 голос
/ 26 июня 2009

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

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

0 голосов
/ 01 октября 2012

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

final int selectedRowIndex = table.rowAtPoint(mouseEvent.getPoint());
final int modelRowIndex = table.convertRowIndexToModel(selectedRowIndex);
0 голосов
/ 26 июня 2009

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

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

0 голосов
/ 26 июня 2009

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

Возвращает ли getSelectedRow () что-нибудь, ДО того, как вы измените модель?

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

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

0 голосов
/ 26 июня 2009

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

...