在开发一个小型任务管理器时,我注意到列没有正确排序。为了解决我的程序问题,我创建了一个最小版本,但它仍然无法正确排序唯一列。
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 <Void, Pair>
{
@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 <Pair> 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 < 100; i++)
{
data [i] = Math.abs (r.nextInt ());
}
}
@Override
public int getColumnCount ()
{
return 1;
}
@Override
public int getRowCount ()
{
return data.length;
}
@Override
public Object getValueAt (int rowIndex, int columnIndex)
{
return data [rowIndex];
}
@Override
public void setValueAt (Object value, int rowIndex, int columnIndex)
{
data [rowIndex] = (Integer) value;
fireTableRowUpdated (rowIndex, columnIndex);
}
@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 (() ->
{
try
{
new TableSortTest ();
}
catch (Exception e)
{
e.printStackTrace ();
}
});
}
}
我尝试使用ScheduledExecutorService
+ Runnable
和Timer
+ TimerTask
来测试它是否是线程问题,但行为是相同的。我还阅读了有关该主题的Java Tutorial页面。鉴于我的表只使用标准类型,我认为一个简单的table.setAutoCreateRowSorter (true);
应该可以完成这项工作,不应该吗?
在每次修改/添加/删除甚至被解雇后,表是否应该对表进行排序?
答案 0 :(得分:2)
使用setSortsOnUpdates()
建议的here @trcs是最好的通用解决方案,但您可以通过选择TableModelEvent
可用于子类的优化来优化更新AbstractTableModel
关键问题是setValueAt()
的实施。如果您的意思是fireTableRowsUpdated()
,而不是fireTableRowUpdated()
,请注意参数表示行的范围,不是行和&amp;柱。在这种情况下,因为“表格行中的所有单元格值都可能已更改”,下面的修订示例将调用fireTableDataChanged()
。我还更改了模型以管理List<Integer>
并将尺寸标准化为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<Void, Pair> {
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<Pair> 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<Integer> 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<Integer>
来优化更新,TableModel
通过process()
传递给<{1}} 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<Integer>, List<Integer>> {
private static final int N = 100;
private final Random r = new Random();
private final List<Integer> data = new ArrayList<>(N);
@Override
protected List<Integer> 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<List<Integer>> chunks) {
for (List<Integer> chunk : chunks) {
model.update(chunk);
}
}
}
private class ATableModel extends AbstractTableModel {
private List<Integer> data = new ArrayList<>();
public void update(List<Integer> 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();
});
}
}
xl
Ubound(xl)
答案 1 :(得分:2)
感谢您的快速回答 trashgod 。你是对的,我的意思是fireTableRowsUpdated ()
但我编写代码时犯了一个错误,抱歉。关键是fireTableRowsUpdated (rowIndex, rowIndex)
和fireTableCellUpdated (rowIndex, columnIndex)
都无法正确排序列。在实际程序中,大多数表行确实从一次迭代变为下一次迭代,因此调用fireTableDataChanged ()
非常有意义。但是我并不想使用它,因为如果我选择一行或多行来向进程发送信号,或者每次更新都会丢失选择。我已经探索过这种方式并找到了两种保留选择的形式,但它有点烦人,其中一种方式会破坏键盘的选择。我接下来会向原始代码展示必要的补充内容。
第一个表单在修改模型之前保存选择,并在每次更新后恢复它:
...
private class Worker extends SwingWorker <Void, Pair>
{
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 <Pair> 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]));
}
}
}
}
...
第二种形式使用ListSelectionListener
,KeyListener
和旗帜。使用键盘进行选择并不起作用。说实话,我不知道我是如何得到这个解决方案的。这可能是偶然的:
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 ()
仅用于在单击表标题时对行进行排序。
问候。