Почему мой ListSelectionListener видит несколько событий, когда я удаляю несколько элементов из списка с помощью removeAll (и e.getValueIsAdjusting == false)?

Я использую EventList и EventTableModel GlazedLists в приведенном ниже примере. Я не уверен, что это имеет значение. У меня есть таблица, за которой я наблюдаю за изменениями выбора. Когда я удаляю несколько элементов, ListSelectionListener видит несколько событий, и, находясь внутри обработчика, выбранные индексы, сообщаемые таблицей, соответствуют состоянию модели до удаления, даже если модель уже была изменена. .

Когда я запускаю приведенный ниже пример, в список добавляется 7 элементов. Если я выберу последние 2 элемента, на консоли появится следующий вывод:

Selected row count: 2
Item list size: 7
Selected index: 5
Selected index: 6

Это то, что я ожидал, но когда я удаляю эти 2 элемента, я получаю следующий вывод:

Selected row count: 1
Item list size: 5
Selected index: 5

Selected row count: 0
Item list size: 5

Поскольку я удаляю элементы в непрерывном блоке, используя removeAll в списке, я предполагаю, что это одно событие, но ListSelectionListener, похоже, получает уведомление, как будто это два отдельных события. Если я удалю 4 элемента, слушатель увидит 4 события.

Таблица и модель не синхронизируются, но я не уверен, почему. Если элементы удаляются из конца списка, выбранные индексы, сообщаемые таблицей, могут быть больше, чем размер базового списка. По сути, индексы, возвращаемые из JTable.getSelectedRows, ненадежны до последнего события выбора, вызванного вызовом removeAll в базовой модели.

Как я могу получать уведомления об изменениях выбора после того, как выбор списка стабилизировался и JTable будет сообщать о правильно выбранных индексах?

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.impl.sort.ComparableComparator;
import ca.odell.glazedlists.swing.EventTableModel;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class MultiDeleteMain {
    // The number of items that should be added to the model.
    @SuppressWarnings("FieldCanBeLocal")
    private final int itemCount = 7;

    private EventList<Item> itemList;
    private JTable itemTable;

    public static void main(String[] args) {
        new MultiDeleteMain();
    }

    public MultiDeleteMain() {
        SwingUtilities.invokeLater(new Runnable() {
            @SuppressWarnings("ConstantConditions")
            @Override
            public void run() {
                // The delete function needs access to the list and table, so
                // they are stored as instance variables.
                itemList = createItemList();
                itemTable = createItemTable(itemList);

                addListSelectionListenerToItemTable(itemTable);

                JPanel mainPanel = new JPanel(new BorderLayout());
                mainPanel.add(createDeleteButton(), BorderLayout.NORTH);
                mainPanel.add(new JScrollPane(itemTable), BorderLayout.CENTER);

                JFrame mainFrame = new JFrame("Multi-deletion in list test.");
                mainFrame.setContentPane(mainPanel);
                mainFrame.pack();
                mainFrame.setSize(300, mainFrame.getHeight());
                mainFrame.setLocationRelativeTo(null);
                mainFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                mainFrame.setVisible(true);
            }
        });
    }

    private EventList<Item> createItemList() {
        EventList<Item> itemList = new BasicEventList<>();
        for (int i = 0; i < itemCount; i++) {
            itemList.add(new Item("Item " + i));
        }
        return itemList;
    }

    @SuppressWarnings("ConstantConditions")
    private JTable createItemTable(EventList<Item> itemList) {
        JTable itemTable = new JTable(new EventTableModel<>(itemList, new EventTableModelFormat()));
        itemTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        return itemTable;
    }

    private void addListSelectionListenerToItemTable(final JTable itemTable) {
        ListSelectionListener listener = new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if(!e.getValueIsAdjusting()) {
                    System.out.println("Selected row count: " + itemTable.getSelectedRowCount());
                    System.out.println("Item list size: " + itemList.size());
                    for(Integer index : itemTable.getSelectedRows()) {
                        System.out.println("Selected index: " + index);
                    }
                    System.out.println();
                }
            }
        };

        itemTable.getSelectionModel().addListSelectionListener(listener);
    }

    private JButton createDeleteButton() {
        JButton deleteButton = new JButton("Delete");
        deleteButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                deleteSelectedItems();
            }
        });
        return deleteButton;
    }

    @SuppressWarnings("ConstantConditions")
    private void deleteSelectedItems() {
        List<Item> itemsToDelete = new ArrayList<>();
        for (Integer rowIndex : itemTable.getSelectedRows()) {
            int convertedIndex = itemTable.convertRowIndexToModel(rowIndex);
            itemsToDelete.add(itemList.get(convertedIndex));
        }

        itemList.removeAll(itemsToDelete);
        itemTable.revalidate();
        itemTable.repaint();
    }

    // Enum for managing table columns
    private static enum Columns {
        NAME("Name", String.class, new ComparableComparator());

        private final String name;
        private final Class type;
        private final Comparator comparator;

        private Columns(String name, Class type, Comparator comparator) {
            this.name = name;
            this.type = type;
            this.comparator = comparator;
        }
    }

    // Each table holds a list of items.
    private static class Item {
        private final String name;

        private Item(String name) {
            this.name = name;
        }
    }

    // Table format for use with the EventTableModel
    private static class EventTableModelFormat implements AdvancedTableFormat<Item> {
        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public String getColumnName(int i) {
            return Columns.values()[i].name;
        }

        @Override
        public Object getColumnValue(Item item, int i) {
            return item.name;
        }

        @Override
        public Class getColumnClass(int column) {
            return Columns.values()[column].type;
        }

        @Override
        public Comparator getColumnComparator(int column) {
            System.out.println("Asked for comparator.");
            return Columns.values()[column].comparator;
        }
    }
}

person Ryan J    schedule 16.09.2012    source источник


Ответы (1)


Нам нужно знать, как выглядят EventList и EventTableModel. Могу поспорить, что модель EventTableModel не гарантирует, что метод deleteAll будет передавать только последовательные элементы. Так что единственный вариант — удалять их по одному, запуская для каждого отдельное событие.

person Jakub Zaverka    schedule 16.09.2012
comment
Ты прав. Одно событие на элемент является основной причиной. В этом случае JTable видит одно событие для каждого удаленного элемента и распространяет эти события в модель выбора. Я думал, что GlazedLists объединяет события для последовательных элементов в одно событие, но оказалось, что может быть неверным< /а>. В GlazedLists также есть EventSelectionModel, поведение которого намного ближе к тому, что я ожидал. - person Ryan J; 17.09.2012