Проблемы интервала выбора 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);
}
});
}
}
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, чтобы вручную вызвать сортировку.