Wrong column being sorted when JTable Header clicked

◇◆丶佛笑我妖孽 提交于 2019-12-18 07:16:18

问题


I have the following code for a RowSorterListener. The purpose of this is to sort a column without affecting any other columns.

import javax.swing.event.RowSorterListener;
import javax.swing.event.RowSorterEvent;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import java.util.List;
import java.util.Arrays;

public class UnrelateData implements RowSorterListener {
    JTable table;
    int columnSorted = -1;
    Object[][] dataStore;

    public UnrelateData(JTable table) {
        this.table = table;
    }

    @Override
    public void sorterChanged(RowSorterEvent e)
    {
        if(e.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
            List<SortKey> keys = e.getSource().getSortKeys();
            for (SortKey key : keys) {
                if (key.getColumn() == -1) {
                    columnSorted = -1;
                    break;
                } else {
                    columnSorted = key.getColumn();
                    break;
                }
            }
            dataStore = getData();
        }

        if(e.getType() == RowSorterEvent.Type.SORTED) {
            List<SortKey> keys = e.getSource().getSortKeys();
            for (SortKey key : keys) {
                if (key.getColumn() == -1) {
                    columnSorted = -1;
                    break;
                } else {
                    columnSorted = key.getColumn();
                    break;
                }
            }

            for(int i = 0; i < table.getColumnCount(); i++) {
                if(i != columnSorted && columnSorted != -1) {
                    for (int j = 0; j < table.getRowCount(); j++) {
                        table.setValueAt(dataStore[i][j], j, i);
                    }
                }
            }
        }
    }

    private Object[][] getData() {
        int columnCount = table.getColumnCount();
        int rowCount = table.getRowCount();
        Object[][] tempData = new Object[columnCount][rowCount];

        for(int i = 0; i < columnCount; i++) {
            for(int j = 0; j < rowCount; j++) {
                tempData[i][j] = table.getValueAt(j, i);
            }
        }

        return tempData;
    };
}

This works well. However, there is one major glitch. If a column is moved and I try to sort a column it doesn't correctly sort the column. Instead, it incorrectly sorts the column in the original place of the column moved.

Whereas it should look like (Where "Column 1" and "Column 2" remain unsorted)

Would someone be able to explain why this occurs and how to fix it?

Note: I do not want to use JTableHeader.reorderingAllowed(false)

Edit

I added the following for loops into my code and tried different variations but it didn't seem to work

Attempt 1

if(e.getType() == RowSorterEvent.Type.SORTED) {
    int[] actualColumn = new int[table.getColumnCount()];
    for(int i = 0; i<table.getColumnCount(); i++){
        actualColumn[i] = table.convertColumnIndexToModel(i);
    }

    int[] actualRow = new int[table.getRowCount()];
    for(int i = 0; i<table.getRowCount(); i++){
        actualRow[i] = table.convertRowIndexToModel(i);
    }

    List<SortKey> keys = e.getSource().getSortKeys();
    for (SortKey key : keys) {
        if (key.getColumn() == -1) {
            columnSorted = -1;
            break;
        } else {
            columnSorted = key.getColumn();
            break;
        }
    }

    for(int i = 0; i < table.getColumnCount(); i++) {
        if(i != columnSorted && columnSorted != -1) {
            for (int j = 0; j < table.getRowCount(); j++) {
                table.setValueAt(dataStore[i][j], actualRow[j], actualColumn[i]);
            }
        }
    }
}

Attempt 2

private Object[][] getData() {
    int columnCount = table.getColumnCount();
    int rowCount = table.getRowCount();

    int[] actualColumn = new int[columnCount];
    for(int i = 0; i<table.getColumnCount(); i++){
        actualColumn[i] = table.convertColumnIndexToModel(i);
    }

    int[] actualRow = new int[rowCount];
    for(int i = 0; i<table.getRowCount(); i++){
        actualRow[i] = table.convertRowIndexToModel(i);
    }

    Object[][] tempData = new Object[columnCount][rowCount];

    for(int i = 0; i < columnCount; i++) {
        for(int j = 0; j < rowCount; j++) {
            tempData[i][j] = table.getValueAt(actualRow[j], actualColumn[i]);
        }
    }

    return tempData;
};

Attempt 3 was both attempt one and two put together


回答1:


  • code in RowSorterListener is designatet to returns the index correctly (from RowSorterListeners event)

  • by default you never need to know ordering from JTables view, all those events are models events,

  • add TableColumnModelListener in the case that you want to trace columnMoved, all events from sorting programatically are painted in JTables view correctly

  • 1st. attemtp without column reordering,

Column NO. - 0 is sorted
Column NO. - 1 is sorted
Column NO. - 2 is sorted
Column NO. - 3 is sorted
Column NO. - 4 is sorted

... and so on
BUILD SUCCESSFUL (total time: 21 seconds)

.

  • 2nd. attempt with column reordering (by mouse dragging)

.

Column NO. - 0 is sorted
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
Column NO. - 1 is sorted
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnSelectionChanged from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
Column NO. - 2 is sorted
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
columnMoved from ColumnModelListener
Column NO. - 3 is sorted
Column NO. - 4 is sorted
Column NO. - 0 is sorted
Column NO. - 1 is sorted
Column NO. - 2 is sorted
BUILD SUCCESSFUL (total time: 10 seconds)
  • 3rd. attempt the same correct output if Swing Timer isn't initialized and all event are made by users hand

  • for example

.

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import javax.swing.*;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.*;
import javax.swing.table.*;

public class SortTest1 {

    private JFrame frame = new JFrame(getClass().getSimpleName());

    private DefaultTableModel model = new DefaultTableModel(10, 5) {
        private static final long serialVersionUID = 1L;

        @Override
        public Class<?> getColumnClass(int column) {
            return getValueAt(0, column).getClass();
        }
    };
    private JTable table = new JTable(model);
    private TableRowSorter<?> sorter;
    private static final Random rnd = new Random();
    private Timer timer;
    private int columnNo = 0;


    public SortTest1() {
        for (int row = model.getRowCount(); --row >= 0;) {
            int i = 20 + row % 20;
            model.setValueAt(row + " " + i, row, 0);
            model.setValueAt(i + row, row, 1);
            model.setValueAt(rnd.nextBoolean(), row, 2);
            model.setValueAt(rnd.nextDouble(), row, 3);
            model.setValueAt(row + " " + i * 1, row, 4);
        }
        table.setAutoCreateRowSorter(true);
        sorter = (TableRowSorter<?>) table.getRowSorter();
        sorter.setSortsOnUpdates(true);
        sorter.addRowSorterListener(new RowSorterListener() {

            @Override
            public void sorterChanged(RowSorterEvent rse) {
                if (rse.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
                    List<SortKey> keys = rse.getSource().getSortKeys();
                    for (SortKey key : keys) {
                        System.out.println("Column NO. - " + key.getColumn() + " is sorted");
                        if (key.getColumn() == 0) {
                            break;
                        } else {
                            break;
                        }
                    }
                }
            }
        });
        frame.add(new JScrollPane(table));
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
            // just handle columnMarginChanged to re-paint headings
            @Override
            public void columnMarginChanged(ChangeEvent e) {
                System.out.println("columnMarginChanged from ColumnModelListener");
            }

            @Override
            public void columnAdded(TableColumnModelEvent e) {
                System.out.println("columnAdded from ColumnModelListener");
            }

            @Override
            public void columnRemoved(TableColumnModelEvent e) {
                System.out.println("columnRemovedfrom ColumnModelListener");
            }

            @Override
            public void columnMoved(TableColumnModelEvent e) {
                System.out.println("columnMoved from ColumnModelListener");
            }

            @Override
            public void columnSelectionChanged(ListSelectionEvent e) {
                System.out.println("columnSelectionChanged from ColumnModelListener");
            }
        });
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        timer = new javax.swing.Timer(1000, updateCol());
        timer.setRepeats(true);
        timer.start();
    }

    private Action updateCol() {
        return new AbstractAction("Sort JTable") {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (columnNo > 4) {
                    columnNo = 0;
                    sorter.setSortKeys(Arrays.asList(new RowSorter.SortKey(columnNo, SortOrder.ASCENDING)));              
                } else {
                    sorter.setSortKeys(Arrays.asList(new RowSorter.SortKey(columnNo, SortOrder.ASCENDING)));
                    columnNo++;
                }
            }
        };
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new SortTest1();
        });
    }
}



回答2:


From your problem description (haven't looked at the code) you probably are not converting indexes reported from view indexes to model index or vice versa.

Cf JTable general description, to quote the relevant part:

Similarly when using the sorting and filtering functionality provided by RowSorter the underlying TableModel does not need to know how to do sorting, rather RowSorter will handle it. Coordinate conversions will be necessary when using the row based methods of JTable with the underlying TableModel. All of JTables row based methods are in terms of the RowSorter, which is not necessarily the same as that of the underlying TableModel. For example, the selection is always in terms of JTable so that when using RowSorter you will need to convert using convertRowIndexToView or convertRowIndexToModel. [...]

You need to be aware that listeners on your table will report in view indexes, not model indexes. If you use a view index to get values from the model, you will experience the problem you are describing.

To handle these conversions, following methods exist in JTable:

  • convertRowIndexToModel
  • convertRowIndexToView
  • convertColumnIndexToModel
  • convertColumnIndexToView

A small example to explain model vs view in a JTable in relation to your problem. The model of a table contains the data. The view is what is shown on the screen. The view maps its columns to columns in the model. When the column is dragged to a different position in the view (i.e. what you see on screen), your model is not changed (i.e. the data in the data container is not changed). What happens is that the mapping from view to model changes.

For example you have three columns A, B and C in your data model and you drag the second column on screen to the first position so that the order becomes B, A, C on screen. What the view does is change its mapping to show column B in the first position, A in the second position and C in the third position. So the mapping was view:1->model:A, view:2->model:B, view:3->model:C and after the dragging becomes view:1->model:B, view:2->model:A, view:3->model:C.

Now back to what I said before. When any listener on a JTable reports indexes (row, column) it does so with view indexes. Now if you want to look up what the value is at those indexes in the model, you first need to translate those view indexes to model indexes using the methods I highlighted before.

So you always need to be aware what indexes you are receiving and what you intend to do with them. If you receive indexes from the table (i.e. the view) and you want to use those to look up values in the model, you first need to translate the indexes using the convertXXXToModel methods.



来源:https://stackoverflow.com/questions/34829921/wrong-column-being-sorted-when-jtable-header-clicked

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!