2011-05-03 53 views
6

我正在嘗試爲JTable創建一個簡單的輸入驗證程序。 我最終重寫了方法:editingStopped()。 問題是該事件不包括有關已更新單元的信息。JTable輸入驗證程序

這是我的 「僞代碼」:

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(..)方法,但我不知道如何獲取單元格的座標。

或者是否有更簡單的方法來創建輸入驗證?

問候 揚K.

+0

沒有,你不想調用setValueAt,所以你不需要座標:-) – kleopatra 2011-05-03 21:53:46

回答

12

第一:在JTable的編輯輸入驗證不能很好地支持。一對夫婦的意見

  • tableChanged在TableModelListener是不要做驗證的好地方,在時間的變化已經發生了這一點(模型通知其的事實聽衆)
  • 因此,無論驗證(驗證)你選擇的方法鉤子,永遠不會回到模型,你會最終在一個無限循環(如你所見)
  • 應用程序提供的CellEditorListeners是相當無用的,因爲a)沒有保證通知序列(JTable可能已經或者尚未更新模型)b)編輯器的生命週期不明確

畢竟這些(不完整,不幸的是;-) no-nos,有點希望:最好的辦法是實現一個自定義的CellEditor,它在stopCellCellEditing中進行驗證:如果新值無效,則返回false並可選提供視覺錯誤反饋。看看JTable.GenericEditor得到的如何可能做

+0

謝謝克列奧帕特拉這個漂亮的和有益的澄清! 我意識到在我的情況下,輸入驗證器是非常不合適的。 不僅適用於我,也適用於用戶。 我決定添加/修改CellEditor。 最後我最終得到了一個DefaultCellEditor和一個JComboBox作爲 構造函數參數。每次用戶想要編輯單元格時,JComboBox都會附帶條目「0」「1」和「 - 」。 這樣就沒有必要檢查有效的輸入和更多 confortable的用戶:-) – 2011-05-03 22:52:34

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

感謝:-)只是有點小心:嚴格來說,您的驗證的實施並不完全正確,因爲它是不允許的有副作用。雖然你可能在這裏擺脫它,因爲副作用只是設置背景。 – kleopatra 2013-04-12 16:57:24

+0

@kleopatra:謝謝,這很好。 – Dave 2013-04-12 18:15:06

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