Проблемы интервала выбора JTable с отсортированной таблицей

У меня есть JTable, который сортируется и фильтруется с помощью RowSorter и обновляется в режиме реального времени. У меня возникла проблема, когда пользователи пытаются создать интервал выбора с помощью мыши (щелчок + перетаскивание по строкам) или с помощью клавиатуры (SHIFT + стрелка вниз несколько раз).

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

Если несколько строк выбраны по одной - все хорошо. То, что выбрал пользователь, остается выбранным. Однако, если пользователь попытается создать интервал выбора, щелкнув и перетащив мышь вниз по нескольким строкам, строки будут выбраны правильно, пока не сработает событие обновления модели таблицы. Затем выбор начинает строиться с текущей выбранной строки.

Я прикрепил небольшое демонстрационное приложение. Попробуйте выбрать несколько строк с помощью мыши - каждую секунду выбор будет поврежден....

Это похоже на ошибку (или нежелательное поведение) в классах Java. Я надеюсь, что кто-то может предоставить творческое решение/обходной путь.

    public class TableTest extends JFrame {
    private static final long serialVersionUID = 1L;

    public TableTest() {
        super("Table Selection Problem");
        setBounds(50, 50, 800, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        // Create a table model
        final DefaultTableModel tableModel = new DefaultTableModel(40,10);

        // Create table row sorter sorted on column 1
        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
        sorter.setSortsOnUpdates(true);
        sorter.setSortKeys(Arrays.asList(new RowSorter.SortKey(1,SortOrder.DESCENDING)));

        // Create a table and bind the sorter
        JTable table = new JTable(tableModel);
        table.setRowSorter(sorter);

        // Bind a list selection listener, this doesn't do anything besides showing the selection change(s) on every table model update
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                System.out.println(e); 
            }
        });


        // place the table in the frame
        getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

        // Simulate some application events that trigger a table update...
        new Thread() {
            public void run() {
                try {
                    final Random r = new Random();
                    for(int x = 0; x < 1000; x++) {
                        // update a table cell (not even an add or delete)
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                tableModel.setValueAt(r.nextInt(1000), r.nextInt(40), r.nextInt(10));
                            }
                        });
                        // wait 1 second before next table update
                        Thread.sleep(1000);
                    }
                } catch(Throwable t) {
                    t.printStackTrace();
                }
            }
        }.start();      
    }


    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                (new TableTest()).setVisible(true);                     
            }
        });     
    }
}

person Graeme Ingleby    schedule 01.10.2014    source источник
comment
Должен сказать, что на самом деле не вижу проблемы. С точки зрения перспективы, выбор был между сортировками, то есть, если я выбираю строку 1-5 и перед ней вставляется новая строка, это смещается вниз. Если новая строка вставляется где-то между ними, то выделение разделяется, чего я и ожидал, поскольку выбор основан на данных, а не на строке (с точки зрения высокого уровня)   -  person MadProgrammer    schedule 02.10.2014
comment
Вот как работает код, когда нет обновления табличной модели при увеличении интервала выбора. Если вы запустите мое тестовое приложение, оно отправляет обновление модели таблицы каждую секунду. Нажмите на строку 2 таблицы и начните перетаскивать мышь вверх/вниз, скажем, для выбора строк со 2 по 7, затем немного вернитесь назад, чтобы были выбраны строки со 2 по 4, затем отступите назад, чтобы были выбраны строки со 2 по 10. Обновление табличной модели происходит в процессе выбора. Выделение привязки (строка 2) удаляется, и выделение теперь начинает расширяться от того места, где когда-либо находилась мышь (когда произошло обновление), до того места, куда оно было перемещено.   -  person Graeme Ingleby    schedule 02.10.2014
comment
Итак, возможно, вам нужно каким-то образом изолировать обновления, чтобы, пока происходит обновление выбора списка, обновления ставились в очередь до тех пор, пока выбор списка не будет завершен, например...   -  person MadProgrammer    schedule 02.10.2014
comment
Просто чтобы уточнить - то, что описывает MadProgrammer (выберите строки 1-5 и новая строка, вставленная до того, как она сдвинется вниз, если новая строка вставлена ​​между разбиениями выбора), также является поведением, которое я ожидал. Моя проблема возникает, когда вы перетаскиваете выделение с помощью мыши в течение определенного периода времени. Быстрый выбор, например. щелчок 2 shift щелчок 10 работает нормально. Это когда вы удерживаете клавишу Shift и продолжаете перетаскивать мышь, что-то идет не так. (если обновление таблицы происходит, когда SelectionModel.isValueAdjusting=true)   -  person Graeme Ingleby    schedule 02.10.2014
comment
Я согласен, что живые обновления во время выбора — плохая идея. ... и я приостанавливаю поток событий, чтобы обновления не происходили, пока пользователь делает выбор, но в полном приложении измененные ячейки мигают (черный фон в течение 8 секунд). Я останавливаю обновления, запускаю процесс выбора, но когда обновляются прошитые ячейки, происходит обновление ячейки таблицы, которое затем прерывает выбор :)   -  person Graeme Ingleby    schedule 02.10.2014


Ответы (1)


Когда я запустил ваш код с помощью JDK7 в Windows 7, я обнаружил, что setSortsOnUpdates(...) вызывает проблемы с данными в таблице. То есть я позволил коду работать некоторое время и заметил, что в столбце 1 не отображаются значения. Затем я увеличил ширину рамки, и внезапно значения появились в отсортированном порядке. Итак, по какой-то причине таблица не перерисовывалась, хотя данные изменились. Когда я прокомментировал setSortsOnUpdates(...), изменения в TableModel появились сразу. Я понятия не имею, в чем проблема.

Затем я изменил подход к ручному вызову сортировки при изменении данных. Однако я делал сортировку только тогда, когда ListSelectionListener не настраивался. Вот код:

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;

    public class TableTest5 extends JFrame {
    private static final long serialVersionUID = 1L;

    private boolean isAdjusting = false;
    private boolean isModelChanged = false;
    TableRowSorter<TableModel> sorter;
    final Random r = new Random();

    public TableTest5() {
        super("Table Selection Problem");
        setBounds(50, 50, 800, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        // Create a table model
        final DefaultTableModel tableModel = new DefaultTableModel(30,3);

        // Create table row sorter sorted on column 1
//        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
        sorter = new TableRowSorter<TableModel>(tableModel);
        sorter.setSortsOnUpdates(true);
        sorter.setSortKeys(Arrays.asList(new RowSorter.SortKey(1,SortOrder.DESCENDING)));

        // Create a table and bind the sorter
        JTable table = new JTable(tableModel);
        table.setRowSorter(sorter);

        // Bind a list selection listener, this doesn't do anything besides showing the selection change(s) on every table model update
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                isAdjusting = e.getValueIsAdjusting();

                if (!isAdjusting && isModelChanged)
                {
                    isModelChanged = false;
                    sorter.sort();
                }
            }
        });


        // place the table in the frame
        getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

        // Simulate some application events that trigger a table update...
        new Thread() {
            public void run() {
                try {
                    //final Random r = new Random();
                    for(int x = 0; x < 1000; x++) {
                        // update a table cell (not even an add or delete)
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {

                                int value = r.nextInt(1000);
                                int row = r.nextInt(30);
                                int column = r.nextInt(5);
                                System.out.println(value + " : " + row + " : " + column);
                                tableModel.setValueAt(value, row, column);

                                if (!isAdjusting)
                                    sorter.sort();
                                else
                                    isModelChanged = true;
                            }
                        });
                        // wait 1 second before next table update
                        Thread.sleep(1000);
                    }
                } catch(Throwable t) {
                    t.printStackTrace();
                }
            }
        }.start();

    }


    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                (new TableTest5()).setVisible(true);
            }
        });
    }
}

Возможно, вы захотите использовать TableModelListener для прослушивания изменений TableModel, чтобы вручную вызвать сортировку.

person camickr    schedule 02.10.2014
comment
Хотя это, похоже, устраняет проблему в примере — если вы добавите строку в модель таблицы (или удалите строку из нее), у вас снова возникнет та же проблема. Этот подход не работает для выбора клавиатуры (SHIFT + стрелки вниз). - person Graeme Ingleby; 02.10.2014