我正在尝试为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。
答案 0 :(得分:12)
首先:不支持JTable编辑的输入验证。几条评论
毕竟那些(不完整,不幸;-)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);
}
}