【问题标题】:How to mark JTable cell input as invalid?如何将 JTable 单元格输入标记为无效?
【发布时间】:2011-11-23 19:05:24
【问题描述】:

如果我采用JTable 并在其模型上指定列的类类型,如下所示:

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

然后,每当用户尝试在表格中输入 double 值时,Swing 会自动拒绝输入并将单元格的轮廓设置为红色。

当有人在单元格中输入“负数或 0”输入时,我希望产生相同的效果。我有这个:

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

这可以防止单元格接受任何非正值,但不会将颜色设置为红色并使单元格保持可编辑状态。

我尝试查看默认情况下 JTable 是如何进行拒绝的,但我似乎找不到它。

如何让它像拒绝非整数输入一样拒绝非正输入?

【问题讨论】:

    标签: java swing validation jtable tablecelleditor


    【解决方案1】:

    private static class JTable.GenericEditor 使用自省来捕获由构造具有无效String 值的特定Number 子类引发的异常。如果您不需要这种通用行为,请考虑将PositiveIntegerCellEditor 创建为DefaultCellEditor 的子类。您的stopCellEditing() 方法会相应地更简单。

    附录:更新为使用 RIGHT 对齐和常见错误代码。

    附录:另见Using an Editor to Validate User-Entered Text

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

    【讨论】:

    • 谢谢,这更干净了。我确实返回并更新了我的代码以减少通用性,因为我只是想在我可以控制其使用的特定地方使用它。
    • @trashgod +1 heytrashgod 使用 documentFilter 代替不让用户输入是个好主意吗?
    • @nachokk:是的,取决于上下文;否则,发出哔哔声的红色框可能会有点烦人。 :-)
    • 我也在想,如果你在setValueAt(row,column) 中有自己的AbstractTableModel,你可以在那里验证它是否是一个业务逻辑,但是你会松开哔哔的红框:P
    【解决方案2】:

    我想通了。覆盖 DefaultCellEditor 并返回 false / 如果给定的数字不是正数,则将边框设置为红色。

    不幸的是,由于 JTable.GenericEditor 是 static w/ default 范围,我无法覆盖 GenericEditor 以提供此功能,并且必须通过一些调整重新实现它,除非有人有一种更好的方法,我想听听。

        @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;
            }
        }
    

    【讨论】:

    【解决方案3】:

    此代码是对已接受答案的一个小改进。如果 用户没有输入任何值,点击另一个单元格应该 允许他选择另一个单元格。接受的解决方案不 允许这样做。

    @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();
    }
    

    此解决方案检查空文本。如果是空文本,我们调用stopCellEditing() 方法。

    【讨论】:

      【解决方案4】:

      因此,我首先创建了一个类比,以使该主题更易于理解。
      我们有一支笔(editor)。这支笔需要一些墨水(编辑器使用的component,组件的示例是JTextFieldJComboBox 等)来书写。

      然后这是一支特殊的笔,当我们想用笔写东西时,我们说话(在 GUI 中的打字行为)告诉它写东西(写在model)。在写出之前,这支笔中的程序会先判断单词是否有效(在stopCellEditing()方法中设置),然后将单词写在纸上(model)。

      想解释一下@trashgod 的答案,因为我在DefaultCellEditor 部分花了4 个小时。

      //first, we create a new class which inherit DefaultCellEditor
      private static class PositiveIntegerCellEditor extends DefaultCellEditor {
      //create 2 constant to be used when input is invalid and valid
          private static final Border red = new LineBorder(Color.red);
          private static final Border black = new LineBorder(Color.black);
          private JTextField textField;
      
      //construct a `PositiveIntegerCellEditor` object  
      //which use JTextField when this constructor is called
          public PositiveIntegerCellEditor(JTextField textField) {
              super(textField);
              this.textField = textField;
              this.textField.setHorizontalAlignment(JTextField.RIGHT);
          }
      //basically stopCellEditing() being called to stop the editing mode  
      //but here we override it so it will evaluate the input before  
      //stop the editing mode
          @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;
              }
      //if no exception thrown,call the normal stopCellEditing()
              return super.stopCellEditing();
          }
      
      //we override the getTableCellEditorComponent method so that
      //at the back end when getTableCellEditorComponent method is  
      //called to render the input, 
      //set the color of the border of the JTextField back to black 
          @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);
          }
      }  
      

      最后,在你的类中使用这行代码来初始化 JTable 来设置你的DefaultCellEditor

      table.setDefaultEditor(Object.class,new PositiveIntegerCellEditor(new JTextField()));
      

      Object.class 表示您希望应用编辑器的列类类型 (你想用那支笔在纸的哪个部分。可以是Integer.classDouble.class等)。
      然后我们在 PositiveIntegerCellEditor() 构造函数中传递new JTextField()(决定您希望使用哪种类型的墨水)。

      如果我有什么误解,请告诉我。希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2019-04-30
        • 1970-01-01
        • 2021-08-28
        • 1970-01-01
        • 2014-01-25
        • 2013-08-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多