7

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例外で終わります。問題は、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(..)メソッドを呼び出す必要がありますが、セルの座標を取得する方法がわかりません。

または、入力ベリファイアを作成するためのより簡単な方法はありますか?

よろしくIoannisK。

4

4 に答える 4

12

まず、JTable編集での入力検証は十分にサポートされていません。コメントのカップル

  • TableModelListenerのtableChangedは、検証を行うのに適した場所ではありません。その時点で、変更はすでに行われています(モデルはリスナーに事実を通知します)
  • 結果として、どの検証(検証)メソッドフックを選択しても、モデルに話しかけることは決してなく、(これまで見てきたように)無限ループに陥ることになります。
  • アプリケーションが提供するCellEditorListenersは、a)通知のシーケンスに関する保証がないため(JTableがモデルを更新しているかどうかにかかわらず)、b)エディターのライフサイクルが明確に定義されていないため、かなり役に立ちません。

これらすべて(不完全、残念ながら;-)いいえ、少し希望があります:最善の策は、stopCellCellEditingで検証を行うカスタムCellEditorを実装することです:新しい値が有効でない場合は、falseを返し、オプションで視覚的なエラーを提供しますフィードバック。JTable.GenericEditorを見て、それがどのように行われるかを理解してください。

于 2011-05-03T21:52:43.200 に答える
4

私のために働いたもの(クレオパトラへの帽子のヒント):

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()));
于 2013-04-12T16:20:30.247 に答える
1

うーん、これにはもっと簡単な解決策があるかもしれません。これを試してみてください、それは私のために働きました。重要なのは、最後に選択したアイテムを記憶してから、現在のアイテムに対して検証を実行することです。入力が間違っている場合は、最後に選択した項目にロールバックして、そのことをユーザーに通知できます。ロールバックは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;
            }
        }
    }
}
于 2016-07-16T17:20:28.057 に答える
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);
     }
          
    }
于 2021-02-23T22:46:10.640 に答える