I am trying to get a JTable
to only accept input in a certain format for each column. For example, in a table with 5 columns the ones at indexes 3 and 4 should only accept numeric values.
I know about RowFilter
and RowFilter.regexFilter(...)
because of this post, but that hides the entire row instead of just discarding the change and even prevents yet empty rows from showing up right after adding them. This is how I've tried to use it (aka an example):
public static void main(String[] args) {
// Create empty table with 5 columns
JTable table = new JTable(new DefaultTableModel(new String[][] {}, new String[] { "String", "String", "String", "Num", "Num" }));
// Create a new table row sorter
TableRowSorter<TableModel> sorter = new TableRowSorter<>(table.getModel());
// Filter columns 4 and 5 to only contain numbers
sorter.setRowFilter(RowFilter.regexFilter("[0-9]+", 3, 4));
// Apply the sorter to the table
table.setRowSorter(sorter);
// Boilerplate JFrame setup in case you want to run this
JFrame frame = new JFrame("MRE");
JPanel contentPane = new JPanel();
JButton addRow = new JButton("Add Row");
addRow.addActionListener((event) -> ((DefaultTableModel) table.getModel()).addRow(new String[] {}));
contentPane.add(new JScrollPane(table));
contentPane.add(addRow);
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(contentPane);
frame.setVisible(true);
}
TL;DR: I want cells to restore their previous value if the one entered isn't allowed or to not even accept bad input in the typing phase.
I managed to do it, by overriding the editingStopped(ChangeEvent e)
method of JTable. You're probably supposed to do that with a TableCellEditor
, but I found this to be easier:
public class MyTable extends JTable {
// Contains the check for each column
private final Map<Integer, Function<String, Boolean>> checks = new HashMap<>();
public STable(TableModel model) {
super(model);
// Fill the map
checks.put(0, (str) -> ...);
checks.put(3, (str) -> ...);
checks.put(4, (str) -> ...);
}
@Override
public void editingStopped(ChangeEvent e) {
String value = getCellEditor().getCellEditorValue().toString();
final Function<String, Boolean> checker = checks.get(getSelectedColumn());
if (checker != null && !checker.apply(value))
getCellEditor().cancelCellEditing();
else
super.editingStopped(e);
}
}
Also, I didn't try it, but camickr's suggestion of overriding getColumnClass(int column)
sounds like it could work well. I didn't try it because I needed more complexity than that.