JTable无法正确返回所选行

时间:2023-01-27 13:41:19

I am working with an extension of the DefaultTableModel as follows:

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

This is the NEW AchievementTableModel after updating it to reflect input from some answers.

这是更新后的新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();
}

My GUI is a JTable that has the following properties:

我的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);

I have a JComboBox that selects which data to display. The TableModel is updated with a call to model.replace(cells) and then runs through the above table creation code again.

我有一个JComboBox,可以选择要显示的数据。通过调用model.replace(cells)更新TableModel,然后再次运行上面的表创建代码。

When selecting a row in the GUI JTable, and printing the table.getSelectedRow() value, I ALWAYS get -1 after changing the table data with a model.replace(cells) call from the first selection, even if I reselect the first JComboBox option. Is there a reason for this that I'm missing? Should I change some code?

当在GUI JTable中选择一行并打印table.getSelectedRow()值时,即使我重新选择第一个JComboBox,在使用来自第一个选择的model.replace(cells)调用更改表数据后,我总是得到-1。选项。这是否有理由让我失踪?我应该更改一些代码吗?

EDIT: The code has changed a lot over trying to answer this question so here is the updated code. The new AchievementTableModel is above.

编辑:代码已经改变了很多,试图回答这个问题所以这里是更新的代码。新的AchievementTableModel就在上面。

This sets up the model and table to be viewed correctly and displayed in a ScrollPane

这会将模型和表设置为正确查看并显示在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 个解决方案

#1


you shouldnt reinitialize your table with a new JTable after you call replace. the fireTableDataChanged() method will alert your existing table that it should repaint. what is happening is that you are looking at the table that you put into the panel, but you are changing the variable to a different instance of JTable. When you query that new, but not visible table, it will give you -1 for the selected row count. it might be helpful if you edit your post to display what is going on in that area of the code.

在调用replace之后,你不应该用新的JTable重新初始化你的表。 fireTableDataChanged()方法将提醒您现有的表应该重绘它。发生的事情是你正在查看放入面板的表,但是你正在将变量更改为不同的JTable实例。当您查询新的但不可见的表时,它将为您提供所选行数的-1。如果您编辑帖子以显示代码区域中发生的情况,则可能会有所帮助。

2nd edit:

instead of this:

而不是这个:

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

do this instead:

改为:

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

you dont need to add the table to a new scrollpane and re-add it to the panel on each model change.

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

#2


Ok, now I'm interested

好的,现在我很感兴趣

It looks like you have to really really clean up your code because there are a lot of reference all around.

看起来您必须真正清理代码,因为周围有很多引用。

The reason you are not seeing the table with a selected index is because each time you create a new JTable the method where you print the selected record still points to the original. Since you're displaying now a "newly" created table the old one prints -1.

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

The reason you get empty table when using the DefaultTableModel is because the vectors are null ( perhaps obtained from the combo ) and thus both the data and the headers disappear from the table.

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

You don't need a subclass if you're using Object[][] as data anyway.

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

So here is a somehow simpler test class that you can see to correct yours.

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

I test it with both, your custom TableModel and the DefaultTableModel

我用你的自定义TableModel和DefaultTableModel测试它

This has nothing to do with your custom table model but the way you're using your references.

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

I hope this helps.

我希望这有帮助。

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

}

Your subclass unchanged.

你的子类没有变化。

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

Try it and see how it doesn't lose the table reference and always print the correct selectedRow.

试一试,看看它是如何不丢失表引用的,并始终打印正确的selectedRow。

JTable无法正确返回所选行

Compare it with your code and fix it from there.

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

#3


Maybe try using

也许尝试使用

super.setDataVector(Vector dataVector, Vector ColumnNames);

super.setDataVector(Vector dataVector,Vector ColumnNames);

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

From JavaDoc

Replaces the current dataVector instance variable with the new Vector of rows, dataVector. Each row is represented in dataVector as a Vector of Object values. columnIdentifiers are the names of the new columns. The first name in columnIdentifiers is mapped to column 0 in dataVector. Each row in dataVector is adjusted to match the number of columns in columnIdentifiers either by truncating the Vector if it is too long, or adding null values if it is too short. Note that passing in a null value for dataVector results in unspecified behavior, an possibly an exception. Parameters: dataVector - the new data vector columnIdentifiers - the names of the columns

使用新的行向量dataVector替换当前的dataVector实例变量。每行在dataVector中表示为Object of Vector值。 columnIdentifiers是新列的名称。 columnIdentifiers中的第一个名称映射到dataVector中的第0列。调整dataVector中的每一行以匹配columnIdentifiers中的列数,如果它太长则截断Vector,或者如果它太短则添加空值。请注意,为dataVector传入null值会导致未指定的行为,这可能是一个异常。参数:dataVector - 新数据向量columnIdentifiers - 列的名称

#4


When it swaps out the data its removing the selection (since the index is now different), you'll need recalculate the selection and set it programmatically.

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

I'll point out that in my experience, this is why I tend to extend AbstractTableModel or out right implement my own TableModel interface from the ground up. Modifying the backing data reference as here, always causes a million problems IMHO.

我将指出,根据我的经验,这就是我倾向于扩展AbstractTableModel或者从头开始实现我自己的TableModel接口的原因。在这里修改后备数据引用,总会导致一百万个问题恕我直言。

#5


When you reorder a JTable you need to keep track of the original indexes in the TableModel for your data, not the current indexes on the JTable. Visually the table may have shifted but the underlying data model has not.

重新排序JTable时,需要跟踪TableModel中数据的原始索引,而不是JTable上的当前索引。在视觉上,表可能已经移位但基础数据模型没有。

#6


Sounds like when changing your selection is lost.

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

Does "getSelectedRow()" return anything "BEFORE" you change the model?

“getSelectedRow()”返回任何“之前”更改模型吗?

If so, then hold that index, change the model and then set that index again.

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

Probably you need to use a custom ListSelectionModel for that

可能你需要为它使用自定义ListSelectionModel

#7


Another thing I thought of, when you do table= new JTable(model); you are changing the table that the variable 'table' is referring to, however that may not automatically cause the new table to be rendered.

我想到的另一件事,当你做table = new JTable(model);您正在更改变量'table'所引用的表,但这可能不会自动导致呈现新表。

If you table is contained within a ScrollPane, you may need to call ScrollPane.setViewportView(table);

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

#8


I had the same problem of getting a -1 always for getSelectedRow(). The problem could have got solved by now. Nonetheless, posting the code which fixed my problem:

我有同样的问题,总是为getSelectedRow()获得-1。这个问题现在可以解决了。尽管如此,发布修复我的问题的代码:

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

#1


you shouldnt reinitialize your table with a new JTable after you call replace. the fireTableDataChanged() method will alert your existing table that it should repaint. what is happening is that you are looking at the table that you put into the panel, but you are changing the variable to a different instance of JTable. When you query that new, but not visible table, it will give you -1 for the selected row count. it might be helpful if you edit your post to display what is going on in that area of the code.

在调用replace之后,你不应该用新的JTable重新初始化你的表。 fireTableDataChanged()方法将提醒您现有的表应该重绘它。发生的事情是你正在查看放入面板的表,但是你正在将变量更改为不同的JTable实例。当您查询新的但不可见的表时,它将为您提供所选行数的-1。如果您编辑帖子以显示代码区域中发生的情况,则可能会有所帮助。

2nd edit:

instead of this:

而不是这个:

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

do this instead:

改为:

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

you dont need to add the table to a new scrollpane and re-add it to the panel on each model change.

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

#2


Ok, now I'm interested

好的,现在我很感兴趣

It looks like you have to really really clean up your code because there are a lot of reference all around.

看起来您必须真正清理代码,因为周围有很多引用。

The reason you are not seeing the table with a selected index is because each time you create a new JTable the method where you print the selected record still points to the original. Since you're displaying now a "newly" created table the old one prints -1.

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

The reason you get empty table when using the DefaultTableModel is because the vectors are null ( perhaps obtained from the combo ) and thus both the data and the headers disappear from the table.

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

You don't need a subclass if you're using Object[][] as data anyway.

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

So here is a somehow simpler test class that you can see to correct yours.

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

I test it with both, your custom TableModel and the DefaultTableModel

我用你的自定义TableModel和DefaultTableModel测试它

This has nothing to do with your custom table model but the way you're using your references.

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

I hope this helps.

我希望这有帮助。

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

}

Your subclass unchanged.

你的子类没有变化。

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

Try it and see how it doesn't lose the table reference and always print the correct selectedRow.

试一试,看看它是如何不丢失表引用的,并始终打印正确的selectedRow。

JTable无法正确返回所选行

Compare it with your code and fix it from there.

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

#3


Maybe try using

也许尝试使用

super.setDataVector(Vector dataVector, Vector ColumnNames);

super.setDataVector(Vector dataVector,Vector ColumnNames);

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

From JavaDoc

Replaces the current dataVector instance variable with the new Vector of rows, dataVector. Each row is represented in dataVector as a Vector of Object values. columnIdentifiers are the names of the new columns. The first name in columnIdentifiers is mapped to column 0 in dataVector. Each row in dataVector is adjusted to match the number of columns in columnIdentifiers either by truncating the Vector if it is too long, or adding null values if it is too short. Note that passing in a null value for dataVector results in unspecified behavior, an possibly an exception. Parameters: dataVector - the new data vector columnIdentifiers - the names of the columns

使用新的行向量dataVector替换当前的dataVector实例变量。每行在dataVector中表示为Object of Vector值。 columnIdentifiers是新列的名称。 columnIdentifiers中的第一个名称映射到dataVector中的第0列。调整dataVector中的每一行以匹配columnIdentifiers中的列数,如果它太长则截断Vector,或者如果它太短则添加空值。请注意,为dataVector传入null值会导致未指定的行为,这可能是一个异常。参数:dataVector - 新数据向量columnIdentifiers - 列的名称

#4


When it swaps out the data its removing the selection (since the index is now different), you'll need recalculate the selection and set it programmatically.

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

I'll point out that in my experience, this is why I tend to extend AbstractTableModel or out right implement my own TableModel interface from the ground up. Modifying the backing data reference as here, always causes a million problems IMHO.

我将指出,根据我的经验,这就是我倾向于扩展AbstractTableModel或者从头开始实现我自己的TableModel接口的原因。在这里修改后备数据引用,总会导致一百万个问题恕我直言。

#5


When you reorder a JTable you need to keep track of the original indexes in the TableModel for your data, not the current indexes on the JTable. Visually the table may have shifted but the underlying data model has not.

重新排序JTable时,需要跟踪TableModel中数据的原始索引,而不是JTable上的当前索引。在视觉上,表可能已经移位但基础数据模型没有。

#6


Sounds like when changing your selection is lost.

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

Does "getSelectedRow()" return anything "BEFORE" you change the model?

“getSelectedRow()”返回任何“之前”更改模型吗?

If so, then hold that index, change the model and then set that index again.

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

Probably you need to use a custom ListSelectionModel for that

可能你需要为它使用自定义ListSelectionModel

#7


Another thing I thought of, when you do table= new JTable(model); you are changing the table that the variable 'table' is referring to, however that may not automatically cause the new table to be rendered.

我想到的另一件事,当你做table = new JTable(model);您正在更改变量'table'所引用的表,但这可能不会自动导致呈现新表。

If you table is contained within a ScrollPane, you may need to call ScrollPane.setViewportView(table);

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

#8


I had the same problem of getting a -1 always for getSelectedRow(). The problem could have got solved by now. Nonetheless, posting the code which fixed my problem:

我有同样的问题,总是为getSelectedRow()获得-1。这个问题现在可以解决了。尽管如此,发布修复我的问题的代码:

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