当一个单元格失去焦点时,Jtable可以保存数据吗?

时间:2021-05-12 22:17:53

The high level: I have a JTable that the user can use to edit data.

高级别:我有一个用户可以用来编辑数据的JTable。

Whenever the user presses Enter or Tab to finish editing, the data is saved (I'm asusming that "saved" really means "the TableModel's setValueAt() method is called".)

每当用户按Enter或Tab完成编辑时,数据就会被保存(我认为“保存”实际上意味着“TableModel的setValueAt()方法被调用”)。

If the user leaves the cell in any other way after making an edit, the new data is not saved and the value stays the way it was. So, for example, if the user changes a value and then clicks on some other widget on the screen, the change doesn't "stick."

如果用户在编辑后以任何其他方式离开单元格,那么新数据将不会被保存,并且该值将保持原样。因此,例如,如果用户更改了一个值,然后单击屏幕上的其他小部件,则该更改不会“粘贴”。

I believe that this is the default behavior for a JTable full of Strings, yes?

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

For a variety of reasons, the desired behavior is for the cell to save any and all edits whenever the user leaves the cell. What's the best/right way to get Swing to do this?

出于各种原因,希望的行为是,当用户离开单元格时,单元格可以保存任何和所有编辑。最好的方法是什么?

3 个解决方案

#1


24  

Table Stop Editing explains whats happening and gives a couple simple solutions.

表停止编辑解释了发生了什么,并给出了几个简单的解决方案。

#2


14  

One of the simple solutions proposed

其中一个简单的解决方案。

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

is good only for String columns. The problem is if I have, for example, Float type of the column being edited, enter an empty string in corresponding cell and then click on any other control of the window – Java throws NullPointerException in CellEditorRemover.propertyChange() method of JTable.java. It uses getCellEditor() call to stop or cancel editing but it returns null in this case. If the value entered is not empty or if I remove terminateEditOnFocusLost flag everything is fine. Probably, the situation described is a bug.

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

I hope I can provide a solution based on one of the previous posts. It’s not so trivial as I supposed before but seems to me it works. I had to inherit my own cell editor from default cell editor and my own text field from JTextField which has FocusListener. This focus listener works fine when editing cell loses a focus, and a focus gained by another control of the window. But in the case of cell selection changes focus listener is “deaf”. That’s why I also have to remember previously valid value before editing start to restore it if the entered value will be invalid.

我希望我能根据之前的一个帖子提供一个解决方案。这并不像我之前想象的那么简单,但在我看来它是可行的。我必须从默认的cell编辑器中继承自己的cell编辑器,并且从具有FocusListener的JTextField中继承我自己的文本字段。当编辑单元失去焦点时,这个焦点监听器可以正常工作,而窗口的另一个控件获得了焦点。但在细胞选择改变的情况下,焦点听者是“聋人”。这就是为什么在编辑开始恢复之前,如果输入的值无效,我还必须记住以前的有效值。

See the code below. Tested with Double, Float and Integer, but I hope this will also work with Byte and String.

请参见下面的代码。使用Double、Float和Integer测试,但我希望这也能处理字节和字符串。

Text field with focus listener:

文本字段与焦点侦听器:

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

Default cell editor class:

默认单元格编辑器类:

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

It the code of table initialization you have to add the following:

它是表初始化的代码,您必须添加以下内容:

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

Hope, this will help somebody who have the same problem.

希望,这将帮助有同样问题的人。

#3


5  

You need to add a focus listener. Given that JTable is basically a container of its cell components, you actually want the focus listener for every cell in your table that needs to behave in the way you indicated.

您需要添加一个焦点侦听器。由于JTable基本上是它的单元格组件的容器,所以您实际上希望您的表中的每个单元的焦点侦听器都需要按照您指示的方式运行。

To do this, you will need to create custom cell editor, which wraps the cell component that has a registered focus listener. And when you get the callback for the loss of focus event, you do the data save, as you require.

要做到这一点,您需要创建自定义的单元格编辑器,该编辑器封装具有注册焦点侦听器的单元格组件。当您获得了焦点事件丢失的回调时,您需要按照您的要求进行数据保存。

This pretty much details most of what you need to do. The details of implementing the focus listener is not there, but that is fairly straightforward.

这是你需要做的大部分细节。实现焦点侦听器的细节不在那里,但这相当简单。

Lets say you do use a JTextComponent as your cell component. Then:

假设您使用JTextComponent作为单元格组件。然后:

public void focusLost(FocusEvent e) {
   JTextComponent cell = (JTextComponent) e.getSource();  
   String data = cell.getText();

   // TODO: save the data for this cell
}

[p.s. edit]:

[注。编辑:

The thread that is calling you with this event is the dispatch thread. Do NOT use it for actions with high latency. But if you are just flipping bits in the heap, it should be ok.

用这个事件调用您的线程是分派线程。不要将其用于高延迟的操作。但是如果你只是在堆中翻转位,它应该是可以的。

#1


24  

Table Stop Editing explains whats happening and gives a couple simple solutions.

表停止编辑解释了发生了什么,并给出了几个简单的解决方案。

#2


14  

One of the simple solutions proposed

其中一个简单的解决方案。

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

is good only for String columns. The problem is if I have, for example, Float type of the column being edited, enter an empty string in corresponding cell and then click on any other control of the window – Java throws NullPointerException in CellEditorRemover.propertyChange() method of JTable.java. It uses getCellEditor() call to stop or cancel editing but it returns null in this case. If the value entered is not empty or if I remove terminateEditOnFocusLost flag everything is fine. Probably, the situation described is a bug.

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

I hope I can provide a solution based on one of the previous posts. It’s not so trivial as I supposed before but seems to me it works. I had to inherit my own cell editor from default cell editor and my own text field from JTextField which has FocusListener. This focus listener works fine when editing cell loses a focus, and a focus gained by another control of the window. But in the case of cell selection changes focus listener is “deaf”. That’s why I also have to remember previously valid value before editing start to restore it if the entered value will be invalid.

我希望我能根据之前的一个帖子提供一个解决方案。这并不像我之前想象的那么简单,但在我看来它是可行的。我必须从默认的cell编辑器中继承自己的cell编辑器,并且从具有FocusListener的JTextField中继承我自己的文本字段。当编辑单元失去焦点时,这个焦点监听器可以正常工作,而窗口的另一个控件获得了焦点。但在细胞选择改变的情况下,焦点听者是“聋人”。这就是为什么在编辑开始恢复之前,如果输入的值无效,我还必须记住以前的有效值。

See the code below. Tested with Double, Float and Integer, but I hope this will also work with Byte and String.

请参见下面的代码。使用Double、Float和Integer测试,但我希望这也能处理字节和字符串。

Text field with focus listener:

文本字段与焦点侦听器:

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

Default cell editor class:

默认单元格编辑器类:

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

It the code of table initialization you have to add the following:

它是表初始化的代码,您必须添加以下内容:

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

Hope, this will help somebody who have the same problem.

希望,这将帮助有同样问题的人。

#3


5  

You need to add a focus listener. Given that JTable is basically a container of its cell components, you actually want the focus listener for every cell in your table that needs to behave in the way you indicated.

您需要添加一个焦点侦听器。由于JTable基本上是它的单元格组件的容器,所以您实际上希望您的表中的每个单元的焦点侦听器都需要按照您指示的方式运行。

To do this, you will need to create custom cell editor, which wraps the cell component that has a registered focus listener. And when you get the callback for the loss of focus event, you do the data save, as you require.

要做到这一点,您需要创建自定义的单元格编辑器,该编辑器封装具有注册焦点侦听器的单元格组件。当您获得了焦点事件丢失的回调时,您需要按照您的要求进行数据保存。

This pretty much details most of what you need to do. The details of implementing the focus listener is not there, but that is fairly straightforward.

这是你需要做的大部分细节。实现焦点侦听器的细节不在那里,但这相当简单。

Lets say you do use a JTextComponent as your cell component. Then:

假设您使用JTextComponent作为单元格组件。然后:

public void focusLost(FocusEvent e) {
   JTextComponent cell = (JTextComponent) e.getSource();  
   String data = cell.getText();

   // TODO: save the data for this cell
}

[p.s. edit]:

[注。编辑:

The thread that is calling you with this event is the dispatch thread. Do NOT use it for actions with high latency. But if you are just flipping bits in the heap, it should be ok.

用这个事件调用您的线程是分派线程。不要将其用于高延迟的操作。但是如果你只是在堆中翻转位,它应该是可以的。