JTable能否在单元格失去焦点时保存数据?

26

高层次:我有一个JTable,用户可以用它来编辑数据。

每当用户按Enter或Tab键完成编辑时,数据都会保存(我假设“保存”实际上是指调用“TableModel”的 setValueAt() 方法)。

如果用户在进行编辑后以任何其他方式离开单元格,则新数据不会保存,数值将保持原样。例如,如果用户更改了一个值,然后点击屏幕上的其他小部件,更改将不会“保留”。

我相信这是一个只包含字符串的JTable的默认行为,对吗?

出于各种原因,期望的行为是,无论何时用户离开单元格,该单元格都会保存任何和所有的修改。如何最好/正确地让Swing实现这一点呢?

3个回答

28

Table Stop Editing解释了正在发生的事情并提供了几个简单的解决方案。


20

提出的简单解决方案之一

table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

这种方法只适用于字符串列。问题在于,如果我有一个浮点型的列正在编辑中,当我在相应的单元格中输入空字符串,然后单击窗口的任何其他控件 - Java会在JTable.javaCellEditorRemover.propertyChange()方法中抛出NullPointerException。它调用getCellEditor()来停止或取消编辑,但在这种情况下返回null。如果输入的值不为空,或者我删除了terminateEditOnFocusLost标志,一切都很好。可能,上述情况是一个bug。

我希望我能根据之前的一个帖子提供一种解决方案。这并不像我之前想象的那样简单,但我认为它可行。 我必须从默认单元格编辑器继承我的自己的单元格编辑器和我的自己的文本字段JTextField。这个焦点监听器在编辑单元格失去焦点时运行良好,并且焦点被窗口的另一个控件获取时也很好。但是,在单元格选择更改的情况下,焦点监听器就会“失聪”。这就是为什么我还要在编辑开始之前记住先前有效的值,以便在输入的值无效时恢复它。

请参见下面的代码。经过测试,可用于DoubleFloatInteger,但我希望这也适用于ByteString

具有焦点监听器的文本框:

public class TextFieldCell extends JTextField {
    public TextFieldCell(JTable cellTable) {
        super();                            // calling parent constructor
        final JTable table = cellTable;     // this one is required to get cell editor and stop editing

        this.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent e) {
            }

            // this function successfully provides cell editing stop
            // on cell losts focus (but another cell doesn't gain focus)
            public void focusLost(FocusEvent e) {
                CellEditor cellEditor = table.getCellEditor();
                if (cellEditor != null)
                    if (cellEditor.getCellEditorValue() != null)
                        cellEditor.stopCellEditing();
                    else
                        cellEditor.cancelCellEditing();
            }
        });
    }
}

默认的单元格编辑器类:

class TextFieldCellEditor extends DefaultCellEditor {
TextFieldCell textField;    // an instance of edit field
Class<?> columnClass;       // specifies cell type class
Object valueObject;         // for storing correct value before editing
public TextFieldCellEditor(TextFieldCell tf, Class<?> cc) {
    super(tf);
    textField = tf;
    columnClass = cc;
    valueObject = null;
}

@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    TextFieldCell tf = (TextFieldCell)super.getTableCellEditorComponent(table, value, isSelected, row, column);
    if (value != null) {
        tf.setText(value.toString());
    }
    // we have to save current value to restore it on another cell selection
    // if edited value couldn't be parsed to this cell's type
    valueObject = value;
    return tf;
}

@Override
public Object getCellEditorValue() {
    try {
        // converting edited value to specified cell's type
        if (columnClass.equals(Double.class))
            return Double.parseDouble(textField.getText());
        else if (columnClass.equals(Float.class))
            return Float.parseFloat(textField.getText());
        else if (columnClass.equals(Integer.class))
            return Integer.parseInt(textField.getText());
        else if (columnClass.equals(Byte.class))
            return Byte.parseByte(textField.getText());
        else if (columnClass.equals(String.class))
            return textField.getText();
    }
    catch (NumberFormatException ex) {

    }

    // this handles restoring cell's value on jumping to another cell
    if (valueObject != null) {
        if (valueObject instanceof Double)
            return ((Double)valueObject).doubleValue();
        else if (valueObject instanceof Float)
            return ((Float)valueObject).floatValue();
        else if (valueObject instanceof Integer)
            return ((Integer)valueObject).intValue();
        else if (valueObject instanceof Byte)
            return ((Byte)valueObject).byteValue();
        else if (valueObject instanceof String)
            return (String)valueObject;
    }

    return null;
}

在表格初始化的代码中,您需要添加以下内容:
myTable.setDefaultEditor(Float.class, new TextFieldCellEditor(new TextFieldCell(myTable), Float.class));
myTable.setDefaultEditor(Double.class, new TextFieldCellEditor(new TextFieldCell(myTable), Double.class));
myTable.setDefaultEditor(Integer.class, new TextFieldCellEditor(new TextFieldCell(myTable), Integer.class));

希望这能帮助遇到同样问题的人。

5
你需要添加焦点监听器。考虑到JTable基本上是其单元格组件的容器,您实际上希望为表中需要以您指定方式行为的每个单元格添加焦点监听器。
为此,您将需要创建自定义单元格编辑器,它包装了具有已注册焦点监听器的单元格组件。当您获得失去焦点事件的回调时,按照您的要求进行数据保存。 这基本上详细说明了您需要做的大部分内容。实现焦点监听器的详细信息不在其中,但这相当简单。
假设您使用JTextComponent作为单元格组件。然后:
public void focusLost(FocusEvent e) {
   JTextComponent cell = (JTextComponent) e.getSource();  
   String data = cell.getText();

   // TODO: save the data for this cell
}

[附言:编辑]:

调用此事件的线程是调度线程。不要在高延迟操作中使用它。但如果您只是在堆中翻转位,那应该没问题。


这就是人类需要从临时媒体中存储的集体知识中学到的教训。 - alphazero
网络档案馆上有 -- 我相应地更改了链接。 - Peter Becker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接