I have a JTable
called transactionList
, each row represents a Transaction
where the first column holds the actual Transaction
object and each subsequent column displays some data about the Transaction
.
I don't want the Transaction
object appearing to the user so I remove it from the ColumnModel
:
TableColumnModel cm = transactionList.getColumnModel();
cm.removeColumn(cm.getColumn(0));
transactionList.setColumnModel(cm);
This works great and I can then retrieve the selected Transaction
using:
(Transaction) transactionList.getModel().getValueAt(transactionList.getSelectedRow(), 0))
The problem begins when the user sorts a column in the table, then the selected row does not match up correctly. I solve this by changing the above line so that we get the selected row from the table directly and not from the model:
(Transaction) transactionList.getValueAt(transactionList.getSelectedRow(), 0));
But now the 0 column is not my hidden Transaction
object but simply its first field.
I will try to explain in another way as well.
Without allowing sorting of the JTable columns, both examples works:
1) Display Transaction object in first column (don't remove from ColumnModel)
Can retrieve Transaction object with:
transactionList.getValueAt(transactionList.getSelectedRow(), 0));
2) Display Transaction object in first column (remove from ColumnModel)
Can retrieve Transaction object with:
transactionList.getModel().getValueAt(transactionList.getSelectedRow(), 0))
If I now allow sorting of the columns, #1 still works. However, method #2 now passes the JTable.getSelectedRow() to TableModel.getValueAt(). These indexes are no longer equal though, the problem is that the JTable and ColumnModel don't contain the Transaction object (only the TableModel does).
transactionList.getColumnCount(); //return 5
transactionList.getColumnModel().getColumnCount(); //return 5
transactionList.getModel().getColumnCount(); //returns 6
A possible solution I see is to sort the TableModel along with the JTable when the user clicks a column header. Is this viable? Are there better ways of achieving this goal (having a "hidden" object attached to a JTable row)?
TableModel
public class TransactionTableModel extends AbstractTableModel {
String[] columnNames = { "<Transaction_Object>", "Date", "Name",
"Hours", "Amount", "Notes" };
public TransactionTableModel() {
}
public String getColumnName(int col) {
return columnNames[col].toString();
}
public Class<?> getColumnClass(int col) {
switch (col) {
case 1:
return Calendar.class;
case 2:
return String.class;
}
}
public int getRowCount() {
return DB.getInstance().getTransactions().size();
}
public int getColumnCount() {
return columnNames.length;
}
public Object getValueAt(int row, int col) {
Transaction t = null;
t = DB.getInstance().getTransactionsChronological().get(row);
switch (col) {
case 0:
return t;
case 1:
return t.getDate();
case 2:
return t.getStudent.getName();
}
}
}
MCVE
There is a hidden first column which has matching values to the second. With an unsorted JTable the button prints the correct value, but after sorting a column by clicking on the header the models are out of sync.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class SwingTesting {
JFrame frame;
TablePane tablePane;
public SwingTesting() {
tablePane = new TablePane();
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JButton test = new JButton("Print hidden item");
test.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
printHiddenItem();
}
});
frame.add(tablePane);
frame.add(test, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
class TablePane extends JPanel {
private final JTable table;
private final TableModel tableModel;
private final ListSelectionModel listSelectionModel;
public TablePane() {
table = new JTable();
tableModel = createTableModel();
table.setModel(tableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.setAutoCreateRowSorter(true);
table.add(table.getTableHeader(), BorderLayout.PAGE_START);
table.setFillsViewportHeight(true);
listSelectionModel = table.getSelectionModel();
table.setSelectionModel(listSelectionModel);
this.add(new JScrollPane(table));
testMethod();
}
private TableModel createTableModel() {
DefaultTableModel model = new DefaultTableModel(new Object[] {
"First", "Second", "Third" }, 0) {
};
addTableData(model);
return model;
}
private void addTableData(DefaultTableModel model) {
model.addRow(new Object[] { "ONE", "ONE", "2007" });
model.addRow(new Object[] { "TWO", "TWO", "2012" });
model.addRow(new Object[] { "THREE", "THREE", "2009" });
model.addRow(new Object[] { "FOUR", "FOUR", "2005" });
model.addRow(new Object[] { "FIVE", "FIVE", "2001" });
}
private void testMethod() {
TableColumnModel cm = table.getColumnModel();
cm.removeColumn(cm.getColumn(0));
table.setColumnModel(cm);
}
}
public void printHiddenItem() {
System.out.println(tablePane.table.getModel().getValueAt(tablePane.table.getSelectedRow(), 0));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingTesting();
}
});
}
}