non-live/non realtime sorting of JTable

682 Views Asked by At

When a table is in sorting mode (using RowSorter), it seems that most of time the table is sorted in realtime. For example, whenever I add new rows, the table is sorted automatically (except editing cell). I am wondering if there is any way to disable this realtime sorting behaviour.

What I want to do is this: After sorting a table, either ascending or descending, then the sorter keeps the current sort state and stops realtime sorting. Then new rows can be added at the bottom, and selected row can be move up and down, and the table updates all changes in view model.

@trashgod: thanks for your answer. I actually kind like native rowsorter method, as I can circle the column state in tri-state (ascending, descending and unsorted). If using Collections.sort, I will have to create a variable to save the original row order before sorting it everytime.

2

There are 2 best solutions below

4
On

You can order the rows of your TableModel independent of RowSorter using Collections.sort() and a custom Comparator, such as the one shown here, that can accept a parameter specifying the sort direction.

enum Sort { ASCENDING, DESCENDING; }
class RecordComparator implements Comparator<Record> {
    public RecordComparator(Sort sort, ...) { ... }
}
0
On

It seems like what you want is to be able to sort the table but then be able to add more rows without the table sort order being changed. Even though it is possible, you'll have to rewrite the DefaultRowSorter and TableRowSorter (source available for both). The reason is that the current implementation is designed to keep a consistent sort order (without considering when the rows were added). You probably won't be able to extend TableRowSorter to overcome this issue since it's parent class has a lot of private functions tied to the design I just described.

What I described above is the clean way. Here is another way you can do it, do this each time you want to insert a new row:

  1. Save the current sort order of the view in a map. I used a hashmap (postion) to map each object in row i of the sort column to it's position in the view. position.put(tableModel.getValueAt(i, column), sorter.convertRowIndexToView(i));

  2. Create a comparator that will use the map created above to sort the incoming data. Set the column comparator of the TableRowSorter to the newly created comparator. Then call sorter.sort(), which is necessary because DefaultRowSorter needs to cache the comparator. The comparator will put the existing objects in their current position and put new objects (created during insertion) at the end.

  3. Perform the row insertion into the table model. In my code I add 3 random Integer, Boolean, and Double values to the 3 columns in my table.

  4. Remove the comparator added in #3 by setting value to null, sorter.setComparator(sortKeys.get(0).getColumn(), null); This way the next time the sort is toggled on the column, it will use the default comparator instead of the dummy one.

Complete code is below. It is intended to add a row to the table each time the button is pressed. Please note I have not tested this with sortkeys > 1 or filters (I'll leave that to you). Let me know what you think of this hack.

private void insertRowButtonActionPerformed(java.awt.event.ActionEvent evt) {
    TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>)jTable1.getRowSorter();
    final HashMap<Object, Integer> position = new HashMap<Object, Integer>();
    List<? extends SortKey> sortKeys = sorter.getSortKeys();

    if(!sortKeys.isEmpty()){
        SortKey sortKey = sortKeys.get(0);
        int column = sortKey.getColumn();
        TableModel tableModel = sorter.getModel();
        int n = sorter.getModelRowCount();
        final SortOrder sortOrder = sortKey.getSortOrder();
        for(int i=0;i<n;i++){
            position.put(tableModel.getValueAt(i, column), sorter.convertRowIndexToView(i));
        }
        //add dummy comparator
        sorter.setComparator(column, new Comparator<Object>(){
            public int compare(Object o1, Object o2) {
                int obj1Position = position.containsKey(o1) ? position.get(o1):position.size();
                int obj2Position = position.containsKey(o2) ? position.get(o2):position.size();
                return sortOrder == SortOrder.DESCENDING ? obj2Position - obj1Position:obj1Position - obj2Position;
            }
        });
        sorter.sort();
    }

    /////////////insert row///////////////////
    DefaultTableModel tableModel = (DefaultTableModel)jTable1.getModel();
    tableModel.addRow(new Object[]{rand.nextInt(), rand.nextBoolean(), rand.nextDouble()});
    /////////////end insert row///////////////

    //remove dummy comparator
    if(!sortKeys.isEmpty()){
        sorter.setComparator(sortKeys.get(0).getColumn(), null);
    }
}