Reputation: 81
I'm trying to create a "simple" JTable which is tied to an ArrayList using a TableModel to extend the AbstractTableModel. My goal is to retain all of the in-built JTable functionality that allows cell editing, row sorting, and column rearranging. The example I provide here does all of that ... BUT ... under a certain sequence of operations, when editing cells and then rearranging table columns by dragging the column header, or sorting the table using the column header buttons, the content of the most-recently-edited cell gets copied into both columns of the table row.
I can reproduce the problem as follows: Run the example. Double-click or press F2 to Edit the content of the top left cell, then press ENTER. Drag the left-hand column header to the right to its opposite position. The value of both cells of the row change to match the selected cell. Similarly, edit the top-left cell and press ENTER. Then click either column header to sort the table. The sort is completed as desired, but the content of the just-edited cell is copied into the other column of the row. Slightly different behavior if you press TAB or ESC to terminate the edit. But the issue still occurs under the "right" sequence of actions.
I assume the issue is related to the need for my TableModel.getValueAt and .setValueAt methods to convert between TableModel column indexes and View column indexes but, for the life of me, I can't figure out how or where to make the conversion.
Any help would be appreciated.
package tableexample;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
public final class TableExample extends JFrame {
public List<REItem> REList;
public JTable tblREList;
public TableExample() {
REList = new ArrayList<>();
REList.add(new REItem("Template1", "Comment1"));
REList.add(new REItem("Template2", "Comment2"));
RETableModel retm = new RETableModel(REList);
tblREList = new JTable(retm);
tblREList.setAutoCreateRowSorter(true);
JScrollPane spREList = new JScrollPane(tblREList);
this.add(spREList); //add the table to the frame
this.setTitle("Table Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
} // end TableExample constructor
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableExample notUsed = new TableExample();
});
} //end main
/**
* One item for the RETable
*/
public final class REItem {
String template;
String comment;
public REItem(String tmp, String cmt) {
this.template = tmp;
this.comment = cmt;
}
public String getTemplate() { return template; }
public String getComment() { return comment; }
public void setTemplate(String value) { this.template = value; }
public void setComment(String value) { this.comment = value; }
} // end class REItem
public class RETableModel extends AbstractTableModel {
private List<REItem> reList = new ArrayList();
private final String[] columnNames = { "Template", "Comment" };
public RETableModel(List<REItem> list){
this.reList = list;
}
@Override
public String getColumnName(int column){
return columnNames[column];
}
@Override
public int getRowCount() {
return reList.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int rowIndex, int column) {
REItem rei = reList.get(rowIndex);
switch (column) {
case 0:
return rei.getTemplate();
case 1:
return rei.getComment();
}
return null; // default case
}
@Override
public boolean isCellEditable(int row, int column) {
return true;
}
@Override
public Class<?> getColumnClass(int column){
switch (column){
case 0:
return String.class;
case 1:
return String.class;
}
return null; // default case
}
@Override
public void setValueAt(Object value, int row, int column) {
REItem rei = reList.get(row);
switch (column) {
case 0:
rei.setTemplate(value.toString());
case 1:
rei.setComment(value.toString());
}
// uncommenting the below often causes IndexOutOfBoundsException: Invalid range exception
fireTableCellUpdated(row, column);
} // end setValueAt
} // end RETableModel
} // end class TableExample
Upvotes: 0
Views: 312
Reputation: 332
This is sounding like a bug in Swing. Nothing in your example code looks wrong. Your table model is fine and if you were doing something wrong it would be in your table... which you are using the default implementation.
The only advice I can give is to try fireTableDataChanged() instead of fireTableCellUpdated(row, column). Without trying your code I think your data is probably fine (the contents of the tablemodel) but that the gui is confused.
I didn't spot the missing break's in setValueAt that others have pointed out.
Upvotes: 0
Reputation: 388
A switch
statement executes all statements that follow a matching case. This means that, once a condition is met, all code bellow in the switch block is executed. This can be avoided by adding break;
to each case. In your example, when you set the template, you also set the comment.
switch (column) {
case 0:
rei.setTemplate(value.toString());
break;
case 1:
rei.setComment(value.toString());
break;
}
Upvotes: 3