Может ли Jtable сохранять данные всякий раз, когда ячейка теряет фокус? - PullRequest
23 голосов
/ 31 октября 2009

Высокий уровень: у меня есть JTable, который пользователь может использовать для редактирования данных.

Всякий раз, когда пользователь нажимает Enter или Tab, чтобы закончить редактирование, данные сохраняются (я предполагаю, что "сохранено" действительно означает "метод setValueAt () TableModel называется".)

Если пользователь покидает ячейку любым другим способом после внесения изменений, новые данные не сохраняются, и значение остается таким же, каким оно было. Так, например, если пользователь изменяет значение и затем нажимает на какой-то другой виджет на экране, изменение не «прилипает».

Я считаю, что это поведение по умолчанию для JTable, полного строк, да?

По ряду причин желаемое поведение заключается в том, чтобы ячейка сохраняла все без исключения изменения, когда пользователь покидает ячейку. Какой лучший / правильный способ заставить Swing сделать это?

Ответы [ 3 ]

26 голосов
/ 31 октября 2009

Редактирование таблицы остановлено объясняет, что происходит, и дает пару простых решений.

14 голосов
/ 13 ноября 2010

Одно из предложенных простых решений

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

подходит только для строковых столбцов. Проблема в том, что если у меня есть, например, тип Float редактируемого столбца, введите пустую строку в соответствующую ячейку и затем нажмите на любой другой элемент управления окна - Java выбрасывает NullPointerException в CellEditorRemover.propertyChange() метод JTable.java , Для остановки или отмены редактирования используется вызов getCellEditor(), но в этом случае возвращается null. Если введенное значение не пустое или если я убрал флаг terminateEditOnFocusLost, все в порядке. Возможно, описанная ситуация является ошибкой.

Я надеюсь, что смогу предложить решение на основе одного из предыдущих постов. Это не так тривиально, как я предполагал раньше, но мне кажется, это работает. Я должен был унаследовать свой собственный редактор ячеек от редактора ячеек по умолчанию и свое собственное текстовое поле от JTextField, который имеет FocusListener. Этот слушатель фокуса работает хорошо, когда редактирование ячейки теряет фокус, а фокус получает другой элемент управления окна. Но в случае изменения выбора ячейки фокус слушателя оказывается «глухим». Вот почему я также должен запомнить ранее действительное значение перед началом редактирования, чтобы восстановить его, если введенное значение будет недействительным.

См. Код ниже. Протестировано с Double, Float и Integer, но я надеюсь, что это также будет работать с Byte и String.

Текстовое поле с фокусом слушателя:

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 голосов
/ 31 октября 2009

Вам нужно добавить слушатель фокуса . Учитывая, что JTable - это, по сути, контейнер компонентов его ячеек, вам действительно нужен слушатель фокуса для каждой ячейки в вашей таблице, которая должна вести себя так, как вы указали.

Чтобы сделать это, вам нужно будет создать собственный редактор ячеек, который обернет компонент ячейки с зарегистрированным слушателем фокуса. И когда вы получаете обратный вызов для события потери фокуса, вы сохраняете данные, как вам требуется.

Это почти все, что вам нужно сделать . Подробности реализации слушателя фокуса отсутствуют, но это довольно просто.

Допустим, вы используете JTextComponent в качестве компонента вашей ячейки. Тогда:

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

   // TODO: save the data for this cell
}

[p.s. править]:

Поток, который вызывает вас с этим событием, является потоком отправки. НЕ используйте его для действий с высокой задержкой. Но если вы просто переворачиваете биты в куче, все должно быть в порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...