¿Cómo marcar la entrada de la celda JTable como inválida?

Si tomo una JTable y especifico el classtype de una columna en su modelo de la siguiente manera:

  DefaultTableModel model = new DefaultTableModel(columnNames, 100) { @Override public Class getColumnClass(int columnIndex) { return Integer.class; }}; 

Entonces, cada vez que un usuario intenta ingresar un valor double en la tabla, Swing rechaza automáticamente la entrada y establece el contorno de la celda en rojo.

Quiero que ocurra el mismo efecto cuando alguien ingresa una entrada ‘negativa o 0’ a la celda. Tengo esto:

  @Override public void setValueAt(Object val, int rowIndex, int columnIndex) { if (val instanceof Number && ((Number) val).doubleValue() > 0) { super.setValueAt(val, rowIndex, columnIndex); } } } 

Esto evita que la celda acepte valores no positivos, pero no establece el color en rojo y deja la celda como editable.

Traté de ver cómo JTable está haciendo el rechazo por defecto, pero parece que no puedo encontrarlo.

¿Cómo puedo hacer que rechace la entrada no positiva de la misma manera que rechaza la entrada no entera?

Gracias

La private static class JTable.GenericEditor utiliza la introspección para detectar las excepciones planteadas al construir subclases Number específicas con valores de String inválidos. Si no necesita dicho comportamiento genérico, considere la posibilidad de crear PositiveIntegerCellEditor como una subclase de DefaultCellEditor . Su método stopCellEditing() sería, en consecuencia, más simple.

Addendum: actualizado para usar la alineación RIGHT y el código de error común.

Adición: Vea también Usar un editor para validar el texto ingresado por el usuario .

enter image description here

  private static class PositiveIntegerCellEditor extends DefaultCellEditor { private static final Border red = new LineBorder(Color.red); private static final Border black = new LineBorder(Color.black); private JTextField textField; public PositiveIntegerCellEditor(JTextField textField) { super(textField); this.textField = textField; this.textField.setHorizontalAlignment(JTextField.RIGHT); } @Override public boolean stopCellEditing() { try { int v = Integer.valueOf(textField.getText()); if (v < 0) { throw new NumberFormatException(); } } catch (NumberFormatException e) { textField.setBorder(red); return false; } return super.stopCellEditing(); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { textField.setBorder(black); return super.getTableCellEditorComponent( table, value, isSelected, row, column); } } 

Me lo imaginé. Anule DefaultCellEditor y devuelva false / establezca el borde en rojo si el número proporcionado no es positivo.

Desafortunadamente, como JTable.GenericEditor es static con default scope default , no puedo anular el GenericEditor para proporcionar esta funcionalidad y tener que volver a implementarla con algunos ajustes, a menos que alguien tenga una mejor manera de hacerlo, lo cual Me gustaría escuchar.

  @SuppressWarnings("serial") class PositiveNumericCellEditor extends DefaultCellEditor { Class[] argTypes = new Class[]{String.class}; java.lang.reflect.Constructor constructor; Object value; public PositiveNumericCellEditor() { super(new JTextField()); getComponent().setName("Table.editor"); ((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT); } public boolean stopCellEditing() { String s = (String)super.getCellEditorValue(); if ("".equals(s)) { if (constructor.getDeclaringClass() == String.class) { value = s; } super.stopCellEditing(); } try { value = constructor.newInstance(new Object[]{s}); if (value instanceof Number && ((Number) value).doubleValue() > 0) { return super.stopCellEditing(); } else { throw new RuntimeException("Input must be a positive number."); } } catch (Exception e) { ((JComponent)getComponent()).setBorder(new LineBorder(Color.red)); return false; } } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { this.value = null; ((JComponent)getComponent()).setBorder(new LineBorder(Color.black)); try { Class type = table.getColumnClass(column); if (type == Object.class) { type = String.class; } constructor = type.getConstructor(argTypes); } catch (Exception e) { return null; } return super.getTableCellEditorComponent(table, value, isSelected, row, column); } public Object getCellEditorValue() { return value; } } 

Este código es una pequeña mejora de la respuesta aceptada. Si el usuario no ingresa ningún valor, al hacer clic en otra celda debería permitirle seleccionar otra celda. La solución aceptada no permite esto.

 @Override public boolean stopCellEditing() { String text = field.getText(); if ("".equals(text)) { return super.stopCellEditing(); } try { int v = Integer.valueOf(text); if (v < 0) { throw new NumberFormatException(); } } catch (NumberFormatException e) { field.setBorder(redBorder); return false; } return super.stopCellEditing(); } 

Esta solución busca texto vacío. En el caso de un texto vacío, llamamos al método stopCellEditing() .