【问题标题】:JTable not returning selected row correctlyJTable 未正确返回所选行
【发布时间】:2009-06-25 20:32:13
【问题描述】:

我正在使用 DefaultTableModel 的扩展,如下所示:

这是更新后的新成就表模型以反映来自某些答案的输入。

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

我的 GUI 是一个具有以下属性的 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() 值时,我总是在使用第一次选择的 model.replace(cells) 调用更改表数据后得到 -1,即使我重新选择第一个 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);

【问题讨论】:

  • 你能显示你打印table.getSelectedRow()的方法吗
  • 有一个附有动作的按钮,它依赖于 getSelectedRow() 方法,并将选定的行打印到 System.out。我选择一行并单击按钮,但总是得到 -1 输出。
  • MMMhh 您不必重写所有这些方法。您正在重复超类已经做的事情,并且可能在此过程中失去了一些听众。评论所有这些方法并按照克林特的建议去做。让我们看看会发生什么。
  • 我删除了所有不需要的额外覆盖方法,因此只剩下 isCellEditable 和替换。然后将构造函数重做为 super(c,co);它没有改变任何东西。
  • 调用table = new JTable的原因是什么,反对table.setModel(model)?

标签: java user-interface jtable selection


【解决方案1】:

在调用 replace 之后,您不应该使用新的 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);
   }

您无需将表格添加到新的滚动窗格并在每次模型更改时将其重新添加到面板中。

【讨论】:

  • 如果我删除了 JTable 的重新创建,当我切换模型时,表会变为空。我应该用什么让表格重新出现?
  • 列也消失了。
  • 听起来好像你也在清理你的表模型。为了让它工作,你可以做的是创建一个新的表模型,而不是创建一个新的 JTable,你可以在表上调用 setModel()。
  • 我在空检查后将 JTable 设置为 setModel 而不是 new JTable,并改为创建新模型。这并没有解决问题,一切仍然消失。
  • 你还在调用replace()吗?每次更改组合时,您是否都在创建新的成就表模型?
【解决方案2】:

好的,我有兴趣了

看起来你必须真正清理你的代码,因为周围有很多参考。

您没有看到带有选定索引的表的原因是每次创建新的JTable 时,打印选定记录的方法仍然指向原始记录。由于您现在显示的是“新”创建的表格,因此旧表格将打印 -1

使用DefaultTableModel 时得到空表的原因是因为向量是null(可能从组合中获得),因此数据和标题都从表中消失了。

如果您使用Object[][] 作为数据,则不需要子类。

所以这里有一个更简单的测试类,你可以看到它来纠正你的。

我用你的自定义 TableModelDefaultTableModel

来测试它

这与您的自定义表格模型无关,而是您使用引用的方式。

我希望这会有所帮助。

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

将其与您的代码进行比较并从那里修复它。

【讨论】:

  • 我将发布我更新的代码,因为在尝试解决这个问题的过程中发生了很多变化。之后,我会看看我可以从您的示例中做出哪些改变。
  • 是的。稍微清理一下(删除敏感信息)并发布。我敢打赌这很容易解决。
  • 原帖中的代码已经更新,请看一下。感谢您迄今为止的帮助。
【解决方案3】:

也许可以尝试使用

super.setDataVector(Vector dataVector, Vector ColumnNames);

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

来自 JavaDoc

替换当前的dataVector 带有新向量的实例变量 行数,dataVector。每一行都是 在 dataVector 中表示为 Vector 对象值。列标识符 是新列的名称。这 columnIdentifiers 中的名字是 映射到 dataVector 中的第 0 列。每个 调整 dataVector 中的行以匹配 中的列数 columnIdentifiers 通过截断 向量如果太长,或者 如果它太短,则添加空值。 请注意,传入一个空值 dataVector 导致未指定 行为,可能是一个例外。 参数:dataVector - 新数据 向量 columnIdentifiers - 名称 列数

【讨论】:

  • 我尝试将替换方法更改为: public void replace(Object[][] c, Object[] co) { setDataVector(convertToVector(c), convertToVector(co)); fireTableDataChanged(); } 并没有解决问题。
  • 当您调用convertToVector时,它是否将其转换为向量的向量?还是数组向量?
  • 各有一个,分别代表数据和列名。
【解决方案4】:

当它交换数据时删除选择(因为索引现在不同),您需要重新计算选择并以编程方式设置它。

我会指出,根据我的经验,这就是为什么我倾向于扩展AbstractTableModel 或从头开始实现我自己的TableModel 接口。像这里一样修改支持数据引用,总是会导致一百万个问题恕我直言。

【讨论】:

  • 我在用 replace(cells) 调用交换数据后再次选择,所以我认为这不是问题。
  • 您在 replace(cells) 调用后再次选择它们作为用户,它仍然返回 -1 的索引?
  • 它只在我调用replace之前返回正确的索引,但之后总是返回-1,即使在JTable中选择了许多不同的行
  • “像这里一样修改后备数据引用,恕我直言,总是会导致一百万个问题。” +1,我也是
  • 我会这样做,但在从 AbstractTableModel 到 DefaultTableModel 的过渡中,似乎有很多幕后工作。你能告诉我哪些部分需要改变和/或如何改变吗?这将是一个很大的帮助。
【解决方案5】:

当您对 JTable 重新排序时,您需要跟踪 TableModel 中数据的原始索引,而不是 JTable 上的当前索引。从视觉上看,表格可能发生了变化,但底层数据模型没有发生变化。

【讨论】:

  • 除非我弄错了,否则当我更改所有数据单元格时会更改 TableModel,调用 fireTableDataChanged() 并使用更新的模型创建一个新的 JTable。
  • 您是创建一个新的 JTable 还是只是重置模型?
  • 我调用 model.replace(cells) 并在调用 table = new JTable(model) 之后立即调用
  • 见下文,我不想要这种行为。
【解决方案6】:

听起来好像更改选择时丢失了。

“getSelectedRow()”是否在您更改模型“之前”返回任何内容?

如果是,则保留该索引,更改模型,然后再次设置该索引。

您可能需要为此使用自定义 ListSelectionModel

【讨论】:

  • 它确实会在模型更改之前返回正确的信息,但我不希望根据之前选择的内容来选择某些东西的行为。更改模型时,我希望用户必须选择一个新行。在我的测试中,在更新模型并创建新表后选择一行将始终给出 -1,无论数据或选择如何。
  • 哦,我明白了!!.. 听者似乎一直在查看旧表模型而不是新表模型。试着环顾四周。检查 ListSelectionListener 并查看该指向的内容或位置。另外,不要直接访问 super.dataVector,应该有一个访问器。
  • 当你要求索引时,你是问当前表还是不小心问旧表?
  • 我只保留一个引用,所以它必须是当前表。
【解决方案7】:

我想到的另一件事是,当您执行table= new JTable(model); 时,您正在更改变量“table”所指的表,但这可能不会自动导致新表被呈现。

如果您的表格包含在 ScrollPane 中,您可能需要调用 ScrollPane.setViewportView(table);

【讨论】:

  • 我尝试添加您建议的代码行,但这并没有影响情况。
【解决方案8】:

我遇到了同样的问题,即 getSelectedRow() 总是得到 -1。这个问题现在可能已经解决了。尽管如此,发布解决我问题的代码:

final int selectedRowIndex = table.rowAtPoint(mouseEvent.getPoint());
final int modelRowIndex = table.convertRowIndexToModel(selectedRowIndex);

【讨论】: