2011-09-23 17 views
9

Si tomo una JTable y especificar ClassType de una columna en ella de modelo de la siguiente manera:¿Cómo marcar la entrada de la celda JTable como inválida?

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

Entonces cada vez que un usuario intenta introducir un valor double en la tabla, Swing rechaza automáticamente la entrada y establece el contorno de la célula para rojo.

Quiero que ocurra el mismo efecto cuando alguien ingresa una entrada 'negativa o 0' en 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 célula aceptar cualquier valor no positivos, pero doens't establecer el color a rojo y salen de la célula como editables.

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 forma que rechaza la entrada que no es entera?

Gracias

Respuesta

14

El private static class JTable.GenericEditor utiliza introspección para capturar las excepciones planteadas por la construcción específicas Number subclases con inválidas String valores. Si no necesita dicho comportamiento genérico, considere crear PositiveIntegerCellEditor como una subclase de DefaultCellEditor. Su método stopCellEditing() sería correspondientemente más simple.

Adición: Actualizado para usar RIGHT alineación y código de error común.

Adición: Vea también Using an Editor to Validate User-Entered Text.

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); 
    } 
} 
+0

Gracias esto es mucho más limpio. Regresé y actualicé mi código para que fuera menos genérico, ya que solo buscaba usarlo en un lugar específico donde pudiera controlar su uso. – Cuga

+0

Ver también esto [alternativa] (http://stackoverflow.com/a/13510756/230513). – trashgod

+0

@trashgod +1 hey trashgod ¿Es una buena idea usar un documentFilter como alternativa para no dejar que los tipos de usuario? – nachokk

2

Lo descubrí. Anule DefaultCellEditor y devuelva false/establezca el borde en rojo si el número proporcionado no es positivo.

desgracia, ya que es JTable.GenericEditor static w/default alcance, soy incapaz de anular el GenericEditor para proporcionar esta funcionalidad y tienen que volver a ponerlo en práctica w/algunos ajustes, a menos que alguien tiene una mejor forma de hacer esto, que 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; 
     } 
    } 
+0

+1 para [genericity] (http://en.wikipedia.org/wiki/Generic_programming). Podría intentar pasar un [Class Literal como Token tipo Runtime] (http://download.oracle.com/javase/tutorial/extra/generics/literals.html) al constructor, pero no es especialmente simple. – trashgod

1

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 debe permitirle seleccionar otra celda. La solución aceptada no lo permite .

@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().

Cuestiones relacionadas