JTable Input Verifier

I am trying to create a simple input verifier for JTable. I ended up redefining the method: editingStopped (). The problem is that the event does not contain information about the updated cell.

This is my "pseudo code":

If (user finished editing a cell) { Check if cell`s value is "1" or "0" or "-" (Karnaugh-Veitch) If (check = false) setValue (cell, ""); } 

The first thing I tried was here:

 table.getModel().addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { inputVerify (e.getColumn(), e.getFirstRow()); } }); public void inputVerify (int column, int row) { boolean verified = true; String field = table.getValueAt(row, column).toString(); if (field != null && field.length() == 1) { if ( !(field.charAt(0) == '0' || field.charAt(0) == '1' || field.charAt(0) == '-' )) verified = false; } else { verified = false; } if (!verified) { table.getModel().setValueAt("", row, column); java.awt.Toolkit.getDefaultToolkit().beep(); } System.out.println ("Column = " + column + " Row = " + row + " Value = " + table.getValueAt(row, column) +" Verified = "+verified); } 

But it ends: StackOverflow Exception. I think the problem is that: setValueAt (..) fires another tableChanged () event and an infinite loop is generated.

Now I tried this here:

  table.getDefaultEditor(Object.class).addCellEditorListener(new CellEditorListener() { // called when editing stops public void editingStopped(ChangeEvent e) { // print out the value in the TableCellEditor System.out.println(((CellEditor) e.getSource()).getCellEditorValue().toString()); } public void editingCanceled(ChangeEvent e) { // whatever } }); 

But, as you can see, I can just get the new cell value, not the "coordinate". I need to call the setValueAt (..) method, but I do not know how to get the coordinates of the cell.

Or is there an easier way to create an input verifier?

Regards Ioannis K.

+6
source share
3 answers

First: input validation in JTable editing is not supported. Some comments

  • tableChanged in TableModelListener is not a good place to check, at that time this change has already occurred (the model notifies its listeners of the fact)
  • as a result, any method of verification (verification) that you have chosen never talks to the model, you will end up in an infinite loop (as you saw)
  • The CellEditorListeners attached by the application are pretty useless because a) there is no guarantee about the notification sequence (JTable may or may not have already been updated by the model) b) the editor life cycle is undefined

After all these (incomplete, unfortunately ;-) no-nos, a little hope: it’s best to implement your own CellEditor, which validates in stopCellCellEditing: if the new value is invalid, return false and, if desired, provide visual feedback with errors. Take a look at JTable.GenericEditor to get an idea of ​​how this can be done.

+12
source

What worked for me (tell the hat to klepatra):

 private class CellEditor extends DefaultCellEditor { InputVerifier verifier = null; public CellEditor(InputVerifier verifier) { super(new JTextField()); this.verifier = verifier; } @Override public boolean stopCellEditing() { return verifier.verify(editorComponent) && super.stopCellEditing(); } } // ... private class PortVerifier extends InputVerifier { @Override public boolean verify(JComponent input) { boolean verified = false; String text = ((JTextField) input).getText(); try { int port = Integer.valueOf(text); if ((0 < port) && (port <= 65535)) { input.setBackground(Color.WHITE); verified = true; } else { input.setBackground(Color.RED); } } catch (NumberFormatException e) { input.setBackground(Color.RED); } return verified; } } // ... table.getColumn("Port").setCellEditor(new CellEditor(new PortVerifier())); 
+4
source

hmm, there may be a simpler solution to this. Please try this, it worked for me. The key must remember the last selected item, and then perform a check on the current item. If the entry is incorrect, you can return to the last selected item and notify the user. Rollback is done using EventQueue.invokeLater (...), so a recursive call to listeners is avoided.

 private final DefaultTableModel dtm = new DefaultTableModel(); private final JTable table = new JTable(dtm); private final Object[] lastItem; private final AtomicInteger lastIndex = new AtomicInteger(-1); private final ItemValidator validator = new ItemValidator(); public YourConstructor() { lastItem = new Object[table.getColumnCount()]; //store last value of selected table item in an array. table.addMouseListener(new MouseAdapter(){ public void mouseClicked(MouseEvent evt){ lastIndex.set(table.getSelectedRow()); int row = lastIndex.get(); for(int i=0;i<lastItem.length;i++){ lastItem[i] = table.getValueAt(row, i); } } }); //for input validation, and database update. dtm.addTableModelListener(new TableModelListener(){ @Override public void tableChanged(TableModelEvent e) { switch(e.getType()){ case TableModelEvent.INSERT: System.out.println("insert"); break; case TableModelEvent.UPDATE: validateUpdate(); break; case TableModelEvent.DELETE: System.out.println("delete"); break; default: break; } } }); } public void validateUpdate(){ String item; for(int i=0;i<lastItem.length;i++) { item = (String)table.getValueAt(lastIndex.get(), i); if(i>1 && i<lastItem.length)//column range to be checked { if(!validator.hasNumericText(item)) { final int col = i; final Object lastObject = lastItem[i]; final int row = lastIndex.get(); //the most important part, to avoid StackOverflow //by using EventQueue, you avoid looping around //the TableModelListener. EventQueue.invokeLater(new Runnable(){ public void run(){ table.setValueAt(lastObject, row, col); } }); System.out.println("Error at " + i); break; } } } } 
0
source

Source: https://habr.com/ru/post/887217/


All Articles