javaswingjtablerowsorter

JTable sorting programmatically only


I have a JTable, that is sortable (made it sortable by calling setAutoCreateRowSorter(true) at initialization). I sort this table programmatically and I would like to disable the default event handling on table header, so that the table can be sorted programmatically only. How to achieve it?

The working piece of code would be:

public class SortTable extends JDialog {

    private JTable table;

    DefaultRowSorter<TableModel, String> sorter;

    public SortTable() {
        JScrollPane scrollPane = new JScrollPane();
        setBounds(0, 0, 300, 200);
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(scrollPane, BorderLayout.CENTER);

        //-------most important stuff-------------------
        table = new JTable();
        table.setAutoCreateRowSorter(true); //enabling sorting
        table.setModel(createModel());
        sorter = (DefaultRowSorter<TableModel, String>)table.getRowSorter(); //store sorter to sort programatically later on
        //-----------------------------------------------

        scrollPane.setViewportView(table);
        JPanel buttonPane = new JPanel();
        buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
        getContentPane().add(buttonPane, BorderLayout.SOUTH);
        buttonPane.add(new JButton(getSortAction()));
    }

    private AbstractAction getSortAction() {
        return new AbstractAction("Sort") {

            @Override
            public void actionPerformed(ActionEvent e) {
                sorter.setSortKeys(Arrays.asList(new SortKey(0,SortOrder.ASCENDING)));
                sorter.sort(); //sorting programatically

            }
        };
    }

    private DefaultTableModel createModel() {
        return new DefaultTableModel(
            new Object[][] {
                {"1", "3"},
                {"5", "2"},
                {"4", null},
            },
            new String[] {
                "A", "B"
            }
        );
    }
}

This example is a JDialog containing a JTable with Sort button. Pressing that button will cause column A to be sorted ascending. However, the button is not the only way to sort the table - we can simply click the table header to change sorting. My question is how to make the button the only way to sort the table. It would be also nice to know how to get rid of the arrow that appears when sorting is changed.


Solution

  • Reading posts on similar questions (like @mKorbel suggested) and experimenting myself I've managed to find a solution. The answer for the main question would be: Use setSortable(int index, boolean sortable) method of DefaultRowSorter. Seems obvious but what is interesting, is that this method disables sorting of a column when using toggleSortOrder(int index) of RowSorter but it's ignored when using sort() method of DefaultRowSorter. Is that a kind of inconsistency? Anyway this leaves the door open for a trick. Now I can do:

            for (int i=0 ; i<table.getColumnCount() ; i++) {
                sorter.setSortable(i, false);
            }
    

    ...and voila - the table cannot be sorted by clicking table headers anymore, but it can be sorted programatically. If anyone faces a weird requirement of hiding those sort order indicator icons like I have, there are to ways. First will take a global effect and I don't like it - set the corresponding properties to UIManager.

            UIManager.put("Table.ascendingSortIcon", new EmptyIcon());
            UIManager.put("Table.descendingSortIcon", new EmptyIcon());
    

    The better one is to decorate default renderer of the TableHeader:

            final TableCellRenderer defaultRenderer = table.getTableHeader().getDefaultRenderer();
            table.getTableHeader().setDefaultRenderer(new TableCellRenderer() {
    
                @Override
                public Component getTableCellRendererComponent(JTable table,
                        Object value, boolean isSelected, boolean hasFocus,
                        int row, int column) {
                    JLabel label = (JLabel)defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                    label.setIcon(null);
                    return label;
                }
    
            });