JTable输入验证器

时间:2011-05-03 17:03:16

标签: swing input jtable verification verify

我正在尝试为JTable创建一个简单的输入验证器。 我最终覆盖了方法:editingStopped()。 问题是该事件不包括有关已更新的单元格的信息。

这是我的“伪代码”:

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

我试过的第一个就是这里:

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

但最终会出现:StackOverflow Exception。我想问题是:setValueAt(..)触发另一个tableChanged()事件,并且正在生成无限循环。

现在,我在这里尝试了这个:

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

但是你可以看到我只能检索单元格的新值,而不是“坐标”。 我需要调用:setValueAt(..)方法,但我不知道如何获取单元格的坐标。

或者是否有更简单的方法来创建输入验证程序?

祝你好运 Ioannis K。

4 个答案:

答案 0 :(得分:12)

首先:不支持JTable编辑的输入验证。几条评论

  • tableChanged中的tableChanged不是一个做验证的好地方,在那个时候已经发生了变化(模型通知其听众这个事实)
  • 因此,无论你选择什么样的验证(验证)方法,永远不要再回到模型,你最终会陷入无限循环(如你所见)
  • 应用程序提供的CellEditorListeners相当无用,因为a)无法保证通知顺序(JTable可能已经或者没有更新模型)b)编辑器的生命周期不明确

毕竟那些(不完整,不幸;-)no-nos,一点希望:最好的办法是实现一个自定义CellEditor,它在stopCellCellEditing中进行验证:如果新值无效,则返回false并可选择提供视觉错误反馈。看看JTable.GenericEditor,了解如何做到这一点

答案 1 :(得分:4)

对我有用的东西(给kleopatra的帽子小费):

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

答案 2 :(得分:0)

  嗯,可能有一个更简单的解决方案。请试试这个,它对我有用。   关键是要记住上次选择的项目,然后对当前项目执行验证。   如果输入错误,您可以回滚到最后选择的项目,并通知用户。   使用EventQueue.invokeLater(...)执行回滚,因此避免了对侦听器的递归调用。

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

答案 3 :(得分:0)

我同意将验证器保留在 JTextField 上下文之外(验证器可以从不是表编辑的其他条目字段中重用)。如果您只想在继续编辑时显示错误消息,可以轻松地将验证码附加到 TableCellEditor 上的 getCellEditorValue() 方法。您可以在文本字段中附加一个掩码,您可以通过传递格式化程序来格式化输入...最小代码如下:

    public class ObjectDefaultCellEditor extends javax.swing.DefaultCellEditor {
        
        protected InputVerifier inputVerifier;
        
         public static final String ERROR_MESSAGE_START = "INVALID: ";  
        /**
         * ObjectAbstractCellEditor constructor comment.
         * @param textField javax.swing.JTextField
         */
        public ObjectDefaultCellEditor(javax.swing.JTextField textField) {
            super(textField);
            super.setClickCountToStart(1);

            textField.setBorder(null);
            editorComponent = textField;
        }
        
        /**
         *  
         * @param textField
         * @param inputVerifier
         */
            public ObjectDefaultCellEditor(JFormattedTextField textField, InputVerifier inputVerifier) {
                this(textField);
                this.inputVerifier = inputVerifier;
                
    //          if (inputVerifier != null) {
    //              textField.setInputVerifier(inputVerifier);
    //          }
                
    //          if (maskFormatter != null) {
    //              maskFormatter.install(textField);    // we did decouple creation, so we can pass formatter as an  option
    //          }
                
            }

    /**
     * called by Jtable to get the cell Object value. This value will then be set in table model
     */
    public Object getCellEditorValue() {
            //  Object value = super.getCellEditorValue();  // this resumes to delegate.getCellEditorValue()
            //  Object formattedValue = formatMyValue( value) ;
        
        // call validator
        if (inputVerifier != null) {
            boolean valid = inputVerifier.verify(editorComponent);
            if (!valid) {
                showErrorMessage ((JTextComponent)editorComponent);     
            }
        }
        
        return delegate.getCellEditorValue();
    }

    /**
     * show error on entry field
     * @param aTextComponent
     */
     private void showErrorMessage(JTextComponent aTextComponent) {
              
              if (! isShowingErrorMessage((JTextComponent)editorComponent)) {
          
                StringBuilder message = new StringBuilder(ERROR_MESSAGE_START);
                message.append("\"");
                message.append(aTextComponent.getText());
                message.append("\"");
                if (aTextComponent.getToolTipText() != null ) {
                    message.append(aTextComponent.getToolTipText());
                }
                aTextComponent.setText(message.toString());
                
              }
     }
          
     private boolean isShowingErrorMessage(JTextComponent aTextComponent){
                return aTextComponent.getText().startsWith(ERROR_MESSAGE_START);
     }
          
    }