java - 更新后 setAutoCreateRowSorter 无法正确对表列进行排序

标签 java swing sorting jtable integer

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

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 + RunnableTimer + TimerTask 只是为了测试它是否是线程问题,但行为是相同的。我还阅读了有关该主题的 Java 教程页面。鉴于我的表仅使用标准类型,我认为一个简单的 table.setAutoCreateRowSorter (true); 应该可以完成这项工作,不是吗?

在每次修改/添加/删除甚至被触发后,表不应该被排序吗?

最佳答案

感谢您的快速回答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()仅用于在单击表头时对行进行排序。

问候。

关于java - 更新后 setAutoCreateRowSorter 无法正确对表列进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36520501/

相关文章:

java - 如何向网站发送查询并解析结果?

java - 在 Java 中的另一个类上使用非静态组合框值

java - Jtable 中的水平分隔线

ios - 根据 NSString 日期对对象进行排序

python - 按层次算法排序

java - Spring Boot 1.5.x 的速度

java - Spring Security 和 Facebook OAuth 2.0 与图形 API 集成

java - 将 mouselistener 添加到 JTextPane 中插入的 JLabel/JButton

Javascript - 从排序 DOM 集合中获得的好处

java - 如何修复 : My Web App forces all users to "interact" with the same list,,而不是为每个用户提供自己的列表