更新后,setAutoCreateRowSorter不会正确排序表列

在开发一个小任务管理器时,我注意到列没有正确排序。 为了解决我的程序问题,我创建了一个最小版本,但它仍然无法正确排序唯一列。

import java.awt.BorderLayout; import java.util.List; import java.util.Random; import javax.swing.*; import javax.swing.table.AbstractTableModel; public class TableSortTest extends JFrame { private final JTable table; private final ATableModel model; public TableSortTest () { setDefaultCloseOperation (EXIT_ON_CLOSE); setSize (1366, 768); setLocationRelativeTo (null); model = new ATableModel (); table = new JTable (); table.setFillsViewportHeight (true); table.setAutoCreateRowSorter (true); table.setModel (model); add (new JScrollPane (table), BorderLayout.CENTER); setVisible (true); Worker worker = new Worker (); worker.execute (); } private class Pair { int index; int value; } private class Worker extends SwingWorker  { @Override protected Void doInBackground () { while (!isCancelled ()) { Random r = new Random (); for (int i = 0; i < 100; i++) { int indice = getIndexInRange (0, 99); Pair p = new Pair (); p.index = indice; p.value = Math.abs (r.nextInt ()); publish (p); } try { Thread.sleep (1000); } catch (InterruptedException ie) { ie.printStackTrace (); } } return null; } @Override public void process (List  items) { for (Pair p : items) { model.setValueAt (p.value, p.index, 0); } } } public static int getIndexInRange (int min, int max) { return (min + (int) (Math.random () * ((max - min) + 1))); } private class ATableModel extends AbstractTableModel { private final Integer [] data; public ATableModel () { data = new Integer [100]; Random r = new Random (); for (int i = 0; i  { try { new TableSortTest (); } catch (Exception e) { e.printStackTrace (); } }); } } 

我尝试使用ScheduledExecutorService + RunnableTimer + TimerTask来测试它是否是一个线程问题,但行为是一样的。 我还阅读了有关该主题的Java Tutorial页面。 鉴于我的表只使用标准类型,我认为这是一个简单的table.setAutoCreateRowSorter (true); 应该做的工作,不应该吗?

在每次修改/添加/删除后,是否应该对表进行排序?

感谢您快速回答垃圾箱 。 你是对的,我的意思是fireTableRowsUpdated ()但是当我编写代码时我犯了一个错误,抱歉。 关键是fireTableRowsUpdated (rowIndex, rowIndex)fireTableCellUpdated (rowIndex, columnIndex)都无法正确排序列。 在实际程序中,大多数表行确实从一次迭代变为下一次迭代,因此调用fireTableDataChanged ()非常有意义。 但是我不想使用它,因为如果我选择一行或多行来向进程发送信号,或者每次更新都丢失选择。 我已经探索过这种方式并发现了两种保留选择的forms,但它有点烦人,其中一种用键盘打破了选择。 我接下来展示原始代码的必要补充。

第一个表单在修改模型之前保存选择,并在每次更新后恢复它:

 ... private class Worker extends SwingWorker  { private int [] selectedRows; @Override protected Void doInBackground () { while (!isCancelled ()) { // Save the selection before modifying the model int x = table.getSelectedRowCount (); if (x > 0) { selectedRows = new int [x]; int [] tableSelection = table.getSelectedRows (); for (int i = 0; i < x; i++) { selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]); } } Random r = new Random (); for (int i = 0; i < table.getRowCount (); i++) { int indice = getIndexInRange (0, table.getRowCount () - 1); Pair p = new Pair (); p.index = indice; p.value = Math.abs (r.nextInt ()); publish (p); } // If I put the code to restore the selection here, it doesn't work... try { Thread.sleep (1000); } catch (InterruptedException ie) { ie.printStackTrace (); } } return null; } @Override public void process (List  items) { for (Pair p : items) { model.setValueAt (p.value, p.index, 1); } // Restore the selection on every update if (selectedRows != null && selectedRows.length > 0) { for (int i = 0; i < selectedRows.length; i++) { table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i])); } } } } ... 

第二种forms使用ListSelectionListenerKeyListener和标志。 使用键盘选择不起作用。 说实话,我不知道我是怎么来得到这个解决方案的。 这可能是偶然的:

 public class TableSortTestSolucionConSelectionListener extends JFrame implements KeyListener { ... private boolean ctrlOrShiftDown = false; private int [] selectedRows; @Override public void keyPressed (KeyEvent e) { ctrlOrShiftDown = e.isControlDown () || e.isShiftDown (); } @Override public void keyReleased (KeyEvent e) { ctrlOrShiftDown = e.isControlDown () || e.isShiftDown (); } @Override public void keyTyped (KeyEvent e) { ctrlOrShiftDown = e.isControlDown () || e.isShiftDown (); } public TableSortTestSolucionConSelectionListener () { ... ListSelectionListener lsl = new ListSelectionListener () { @Override public void valueChanged (ListSelectionEvent e) { if (!e.getValueIsAdjusting ()) { if (!ctrlOrShiftDown) { int x = table.getSelectedRowCount (); if (x > 0) { selectedRows = new int [x]; int [] tableSelection = table.getSelectedRows (); for (int i = 0; i < x; i++) { selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]); } } } // Disable the listener to avoid infinite recursion table.getSelectionModel ().removeListSelectionListener (this); if (selectedRows != null && selectedRows.length > 0) { for (int i = 0; i < selectedRows.length; i++) { table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i])); } } table.getSelectionModel ().addListSelectionListener (this); } } }; table.getSelectionModel ().addListSelectionListener (lsl); ... } 

幸运的是,今天我找到了一种简单的方法来正确排序列并保持当前选择。 您只需在代码中添加以下内容:

 TableRowSorter trs = (TableRowSorter) table.getRowSorter (); trs.setSortsOnUpdates (true); 

有了这个, fireTableCellUpdated ()fireTableRowsUpdated ()都按预期工作。 据我所知, setAutoCreateRowSorter ()仅用于在单击表头时对行进行排序。

问候。

使用setSortsOnUpdates()建议的setSortsOnUpdates()是最好的通用解决方案,但您可以通过选择可用于AbstractTableModel子类的TableModelEvent来优化更新。

关键问题是setValueAt()的实现。 如果您的意思是fireTableRowsUpdated() ,而不是fireTableRowUpdated() ,请注意参数表示一系列行, 而不是行和列。 在这种情况下,因为“表中行中的所有单元格值都可能已更改”,下面的修订示例将调用fireTableDataChanged() 。 我还更改了模型以管理List并将大小N标准化。

图片

 import java.awt.BorderLayout; import java.awt.Dimension; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.*; import javax.swing.table.AbstractTableModel; /** @see https://stackoverflow.com/a/36522182/230513 */ public class TableSortTest extends JFrame { private final JTable table; private final ATableModel model; public TableSortTest() { setDefaultCloseOperation(EXIT_ON_CLOSE); model = new ATableModel(); table = new JTable(model){ @Override public Dimension getPreferredScrollableViewportSize() { return new Dimension(200, 500); } }; table.setFillsViewportHeight(true); table.setAutoCreateRowSorter(true); add(new JScrollPane(table), BorderLayout.CENTER); pack(); setLocationRelativeTo(null); setVisible(true); Worker worker = new Worker(); worker.execute(); } private class Pair { int index; int value; } private class Worker extends SwingWorker { private static final int N = 100; private final Random r = new Random(); @Override protected Void doInBackground() { while (!isCancelled()) { for (int i = 0; i < N; i++) { int index = r.nextInt(N); Pair p = new Pair(); p.index = index; p.value = Math.abs(r.nextInt()); publish(p); } try { Thread.sleep(1000); } catch (InterruptedException ie) { ie.printStackTrace(); } } return null; } @Override public void process(List items) { for (Pair p : items) { model.setValueAt(p.value, p.index, 0); } } } private class ATableModel extends AbstractTableModel { private static final int N = 100; private final List data = new ArrayList<>(N); public ATableModel() { final Random r = new Random(); for (int i = 0; i < N; i++) { data.add(Math.abs(r.nextInt())); } } @Override public int getColumnCount() { return 1; } @Override public int getRowCount() { return data.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { return data.get(rowIndex); } @Override public void setValueAt(Object value, int rowIndex, int columnIndex) { data.set(rowIndex, (Integer) value); fireTableDataChanged(); } @Override public Class getColumnClass(int columnIndex) { return Integer.class; } @Override public String getColumnName(int col) { return "Column"; } } public static final void main(String[] args) { SwingUtilities.invokeLater(() -> { new TableSortTest(); }); } } 

认识到这只是一个示例,下面的变体通过发布List优化更新, List通过process()传递给TableModel

 import java.awt.BorderLayout; import java.awt.Dimension; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.*; import javax.swing.table.AbstractTableModel; /** * @ see https://stackoverflow.com/a/36522182/230513 */ public class TableSortTest extends JFrame { private final JTable table; private final ATableModel model; public TableSortTest() { setDefaultCloseOperation(EXIT_ON_CLOSE); model = new ATableModel(); table = new JTable(model) { @Override public Dimension getPreferredScrollableViewportSize() { return new Dimension(200, 500); } }; table.setFillsViewportHeight(true); table.setAutoCreateRowSorter(true); add(new JScrollPane(table), BorderLayout.CENTER); pack(); setLocationRelativeTo(null); setVisible(true); Worker worker = new Worker(); worker.execute(); } private class Worker extends SwingWorker, List> { private static final int N = 100; private final Random r = new Random(); private final List data = new ArrayList<>(N); @Override protected List doInBackground() throws Exception { while (!isCancelled()) { data.clear(); for (int i = 0; i < N; i++) { data.add(Math.abs(r.nextInt())); } publish(data); try { Thread.sleep(1000); } catch (InterruptedException ie) { ie.printStackTrace(System.err); } } return data; } @Override protected void process(List> chunks) { for (List chunk : chunks) { model.update(chunk); } } } private class ATableModel extends AbstractTableModel { private List data = new ArrayList<>(); public void update(List data) { this.data = data; fireTableDataChanged(); } @Override public int getColumnCount() { return 1; } @Override public int getRowCount() { return data.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { return data.get(rowIndex); } @Override public Class getColumnClass(int columnIndex) { return Integer.class; } @Override public String getColumnName(int col) { return "Column"; } } public static final void main(String[] args) { SwingUtilities.invokeLater(() -> { new TableSortTest(); }); } }