Ячейка не будет обновляться после вызова действия в редакторе ячеек

Я использую таблицу с средством визуализации ячеек, которое позволяет мне размещать кнопку «x» в каждой ячейке со значением, чтобы я мог удалить значение ячейки, нажав кнопку. Редактор ячеек назначает прослушиватель действий кнопке, и при нажатии кнопки вызываются соответствующие действия.

Каждый столбец в модели таблицы, которая является DefaultTableModel, заполняется списком значений массива. Мне успешно удается удалить правильное значение из списка массивов, нажав кнопку, и каждая другая ячейка в таблице обновляется после изменения, но ячейка, значение которой было удалено, остается неизменной. Если я перезапускаю приложение или добавляю значение в список массивов, таблица обновляется, как и ожидалось, и все выглядит так, как должно.

Тогда моя проблема заключается в том, что средство визуализации ячеек не забудет начальное значение ячейки и, следовательно, все еще продолжает печатать это значение в ячейке, даже если таблица перерисовывается. Кто-нибудь знает, как правильно обновить таблицу, чтобы значения отображались правильно?

Класс таблицы:

public class Table extends JTable {

public static DefaultTableModel model = new DefaultTableModel();
private Days d;
private JFrame frame;
private AddCellRenderer renderer;
private AddCellEditor editor;

public Table(Days d, JFrame frame) {
    // Assign the table model to the table
    super(model);
    this.d = d;
    this.frame = frame;

    // Define dimensions of table
    model.setRowCount(11);
    model.setColumnCount(5);

    // Block resizing and reordering of headers
    this.setColumnSelectionAllowed(false);
    this.setRowSelectionAllowed(false);
    this.setCellSelectionEnabled(true);
    this.setTableHeader(null);

    // Define first row height
    this.setRowHeight(38);
    // Define all other row's heights
    this.setRowHeight(0, 45);

    this.d.setTable(this);

    for (int i = 0; i < 5; i++) {
        editor = new AddCellEditor(new JCheckBox(), d.getVisibleDays().get(i), this);
        renderer = new AddCellRenderer(d.getVisibleDays().get(i), editor);
        this.getColumnModel().getColumn(i).setCellRenderer(renderer);
        this.getColumnModel().getColumn(i).setCellEditor(editor);
    }

    this.refresh();
}

public void refresh() {

    // Empty table
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 11; j++) {
            this.getModel().setValueAt(null, j, i);
        }
    }

    // Populate table with entries for each day
    for (int i = 0; i < 5; i++) {

        Iterator<String> iterator = d.getVisibleDays().get(i).getEntryListIterator();

        int j = 0;

        while(iterator.hasNext()){
            String s = iterator.next();
            this.getModel().setValueAt(s, (j+1), i);
            j++;
        }

        this.getModel().setValueAt(d.getVisibleDays().get(i).getDayName(), 0, i);
    }
}

public void modelClearValueAt(int row, int column) {
    this.getModel().setValueAt(null, row, column);
}
}

Класс редактора ячеек:

public class AddCellEditor extends DefaultCellEditor {

private JPanel headerPanel;
private JPanel entryPanel;
private JLabel dayName;
private JLabel entryValue;
protected JButton addButton;
private JButton removeButton;
private String label;
private int row;
private int column;
private Day day;
private String date;
private Table table;
private AddCellRenderer renderer;

public AddCellEditor(JCheckBox checkBox, Day day, Table table) {
    super(checkBox);
    this.day = day;
    this.table = table;
    date = day.getDayDate();
    headerPanel = new JPanel();
    headerPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    entryPanel = new JPanel();
    entryPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    dayName = new JLabel();
    entryValue = new JLabel();
    addButton = new JButton();
    removeButton = new JButton();
    headerPanel.add(dayName);
    headerPanel.add(addButton);
    entryPanel.add(entryValue);
    entryPanel.add(removeButton);
    addButton.setOpaque(true);
    addButton.setPreferredSize(new Dimension(16, 16));
    removeButton.setOpaque(true);
    removeButton.setPreferredSize(new Dimension(16, 16));
    addButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Add pressed");
            addItem();
            fireEditingStopped();
        }
    });
    removeButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Remove pressed");
            removeItem();
            getTable().refresh();
            fireEditingStopped();
        }
    });

}

public void setRenderer(AddCellRenderer renderer) {
    this.renderer = renderer;
}

public Component getTableCellEditorComponent(JTable table, 
        Object value, boolean isSelected, int row, int column) {

    this.row = row;
    this.column = column;

    if (row == 0) {
         return this.headerPanel;
    }
    if (row != 0 && value != null) {
        return this.entryPanel;
    }
    return null;
}

public void addItem() {
    String input = JOptionPane.showInputDialog("Add entry for " + label + ":");

    if ((input != null) && (input.length() > 0)) {
        System.out.println("Added: " + input + "Item");
        day.addEntry(input, column);
    }
}

public void removeItem() {
    table.modelClearValueAt(row, column);
    day.removeEntry(row-1);
    System.out.println("Item removed");
}

public Table getTable() {
    return this.table;
}

public boolean stopCellEditing() {
    return super.stopCellEditing();
}

protected void fireEditingStopped() {
    super.fireEditingStopped();
}
}

заранее спасибо


person Community    schedule 14.01.2012    source источник
comment
Я добавил тег Swing, чтобы ваш вопрос привлек внимание профессионалов Swing, но для этого мне пришлось удалить один из других тегов (JTable), поскольку вопрос может содержать только 5 тегов. Вы можете изменить это и поменять местами теги, если хотите, но я считаю, что очень важно, чтобы вы привлекли к работе экспертов Swing, чтобы максимизировать свои шансы на получение достойной и своевременной помощи.   -  person Hovercraft Full Of Eels    schedule 14.01.2012
comment
Звучит неплохо! Я ценю вашу помощь.   -  person    schedule 14.01.2012
comment
визуализатор ячеек показывает, что находится в модели, и ничего больше :-) Скорее всего, ваш редактор является недопустимой реализацией: он не должен делать ничего необычного, просто уведомляет своих слушателей, когда он завершается, и возвращает соответствующий getEditorValue (). Ничего другого, в частности, никаких каких-либо изменений исходных данных.   -  person kleopatra    schedule 14.01.2012
comment
Забыл: это помимо анализа @HovercraftFullOfEels — все модификации на уровне данных должны происходить через TableModel, которая, в свою очередь, может обновлять базовые списки (при условии, что они сами по себе не наблюдаемы — в этом случае можно изменить их и позволить tableModel слушает изменения списка).   -  person kleopatra    schedule 14.01.2012
comment
не имеет отношения к вашей проблеме: не расширяйте JSomething, все конкретные реализации предназначены для использования   -  person kleopatra    schedule 14.01.2012
comment
Спасибо за ответы! Я посмотрю на это и, надеюсь, смогу решить эту проблему :) Еще один вопрос: почему добавление записей в таблицу путем добавления значения в нижний список, а затем перерисовка таблицы работает, но не удаляется? Это сводило меня с ума!   -  person    schedule 14.01.2012
comment
это не работает в каком-либо полезном смысле, это чисто случайно: это неправильно, так что просто не делайте, однако, кажется, что вы делаете что-то отдаленно правильное :-)   -  person kleopatra    schedule 14.01.2012


Ответы (2)


ладно, не удержался (альтернатива - помыть ванную :-)

Ниже приведен очень грубый план (достаточный, чтобы дать вам представление) о том, как решить вашу задачу с помощью редактора с хорошим поведением и пользовательской модели таблицы.

  • MyTableModel - это пользовательская реализация, в основном поддерживаемая списком произвольных записей.
  • эта tableModel поддерживает удаление/добавление элементов. Пока что в частном порядке, чтобы у вас не возникло соблазна вызывать их из редактора :-) Они достаточно безопасны, чтобы при необходимости выставлять их на всеобщее обозрение, т.е. если другие части программы также должны изменить записи
  • модель может обрабатывать различные типы значений в setValueAt: маркер Modify, NewEntry или простое значение
  • пользовательский редактор представляет собой панель с кнопками удалить, добавить строку.
  • действия кнопки устанавливают соответствующее значение editorValue перед вызовом editStopped/canceled по мере необходимости.

Модель:

public static class MyTableModel extends AbstractTableModel {

    public enum Modify {
        ADD,
        REMOVE
    }

    public static class NewItem {
        public final Object entry;

        public NewItem(Object entry) {
            this.entry = entry;
        }
    }

    private List entries;

    public MyTableModel(List entries) {
        this.entries = entries;
    }

    @Override
    public int getRowCount() {
        return entries.size();
    }

    @Override
    public int getColumnCount() {
        return 1;
    }


    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        return entries.get(rowIndex);
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (aValue instanceof NewItem) {
            addEntry(((NewItem) aValue).entry);
        } else if (Modify.REMOVE == aValue) {
            removeEntry(rowIndex);
        } else {
            entries.set(rowIndex, aValue);
        }    
    }

    private void removeEntry(int rowIndex) {
        entries.remove(rowIndex);
        fireTableRowsDeleted(rowIndex, rowIndex);
    }

    private void addEntry(Object aValue) {
        int last = getRowCount();
        entries.add(aValue);
        fireTableRowsInserted(last, last);
    }


}

Редактор:

public static class MyCellEditor extends AbstractCellEditor implements TableCellEditor {

    private Object editorValue;
    private JLabel entryView;
    JComponent editor;

    public MyCellEditor() {
        editor = new JPanel();
        entryView = new JLabel();
        editor.add(entryView);
        Action add = createAddAction();
        editor.add(new JButton(add));
        Action remove = createRemoveAction();
        editor.add(new JButton(remove));
    }

    public Action createRemoveAction() {
        Action remove = new AbstractAction("Remove Entry") {

            @Override
            public void actionPerformed(ActionEvent e) {
                editorValue = MyTableModel.Modify.REMOVE;
                fireEditingStopped();
            }

        };
        return remove;
    }

    public Action createAddAction() {
        Action add = new AbstractAction("Add Entry") {

            @Override
            public void actionPerformed(ActionEvent e) {
                String input = JOptionPane.showInputDialog("Add entry: ");

                if ((input != null) && (input.length() > 0)) {
                    editorValue = new MyTableModel.NewItem(input);
                    fireEditingStopped();
                } else {
                    fireEditingCanceled();
                }

            }

        };
        return add;
    }

    @Override
    public Object getCellEditorValue() {
        return editorValue;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table,
            Object value, boolean isSelected, int row, int column) {
        entryView.setText(value != null ? value.toString() : "");
        return editor;
    }


}

Использование:

    List entries = new ArrayList();
    for (int i = 0; i < 5; i++) {
        entries.add("entry " + i);
    }
    TableModel model = new MyTableModel(entries);
    JTable table = new JTable(model);
    table.getColumnModel().getColumn(0).setCellEditor(new MyCellEditor());
    table.getColumnModel().getColumn(0).setPreferredWidth(200);
    table.setRowHeight(50);
person kleopatra    schedule 14.01.2012
comment
большое спасибо! это именно та блестящая простота, к которой я стремился все время, но моих навыков просто недостаточно :) Это мне очень помогло, я думаю, что теперь я перестрою свое приложение вокруг этой логики. Я ценю, что вы нашли это время, чтобы помочь мне вместо того, чтобы мыть ванную, так как у меня есть крайний срок для этой заявки завтра, так что это действительно очень помогло мне! - person ; 14.01.2012
comment
@Edward: вы захотите проголосовать за этот ответ (как я только что сделал) и принять его, нажав на галочку слева от ответа Клео. - person Hovercraft Full Of Eels; 14.01.2012
comment
@Edward: Ты также захочешь почистить для Клео ванную комнату. Это меньшее, что ты можешь сделать, чтобы отплатить ей. - person Hovercraft Full Of Eels; 14.01.2012

Если вы используете DefaultTableModel, вам придется удалить значение, хранящееся в самой модели, а не значение в ArrayList, которое использовалось для построения модели. Это можно сделать, вызвав метод setValueAt(...) объекта DefaultTableModel.

Вы можете опубликовать SSCCE для получения более конкретной помощи.

person Hovercraft Full Of Eels    schedule 14.01.2012
comment
Спасибо за ответ! Вы имеете в виду использование таких методов, как firetabledatachanged, для достижения этой цели? - person ; 14.01.2012
comment
@EdwardLandtman: нет, я имею в виду вызов метода setValueAt(...) объекта DefaultTableModel. Преимущество использования DefaultTableModel заключается в том, что вам не нужно вызывать методы огня, поскольку они уже написаны для вызова. Недостатком является то, что вы не можете использовать свою собственную коллекцию в качестве ядра данных модели (для этого вам нужно будет использовать AbstractTableModel, а затем при необходимости обязательно вызывать методы fireXXX). - person Hovercraft Full Of Eels; 14.01.2012
comment
К сожалению, я новичок в java, поэтому иногда сталкиваюсь с очень простыми проблемами. Когда моя кнопка удаления нажата, я вызываю метод, который удаляет элемент из списка массивов и перерисовывает таблицу, используя значения списка. Я пытался использовать метод setValueAt(...) для установки нулевого значения в рассматриваемой ячейке как до, так и после вызова метода удаления, но безуспешно. Я опубликую SSCCE, чтобы дать вам лучшее представление о моей проблеме. Но спасибо за уже оказанную помощь! - person ; 14.01.2012
comment
@Edward: я считаю, что если вы измените коллекцию, на которой была основана DefaultTableModel, вы можете получить непредсказуемые результаты. Я думаю, что SSCCE действительно был бы полезен. Добро пожаловать, конечно, и удачи вам в этом! - person Hovercraft Full Of Eels; 14.01.2012
comment
Теперь я разместил код для таблицы и модели таблицы, а также код класса редактора ячеек. Я бы сказал, что должно работать удаление элементов из коллекции, поскольку я всегда очищаю таблицу и перерисовываю ее на основе обновленной коллекции. Хотя я могу ошибаться. Надеюсь, у вас есть идеи для меня! - person ; 14.01.2012
comment
@EdwardLandtman, это не sscce (не подходит для автономной работы) - person kleopatra; 14.01.2012
comment
@Edward: Я должен согласиться с Клеопатрой в этом (не только потому, что она знает больше Java и Swing, чем я когда-либо знал), но мы никак не можем запустить опубликованный код. Вам следует еще раз прочитать спецификацию SSCCE. - person Hovercraft Full Of Eels; 14.01.2012