简体   繁体   English

单击JTable标题时排序的列错误

[英]Wrong column being sorted when JTable Header clicked

I have the following code for a RowSorterListener . 我有以下代码用于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. 而是,它在移动的列的原始位置中对列进行了错误的排序。

切换第2列和第3列

Whereas it should look like (Where "Column 1" and "Column 2" remain unsorted) 看起来应该是这样(“第1列”和“第2列”仍未排序)

列2和3正确切换

Would someone be able to explain why this occurs and how to fix it? 有人能够解释为什么会发生这种情况以及如何解决吗?

Note: I do not want to use JTableHeader.reorderingAllowed(false) 注意:我不想使用JTableHeader.reorderingAllowed(false)

Edit 编辑

I added the following for loops into my code and tried different variations but it didn't seem to work 我在代码中添加了以下for循环,并尝试了不同的变体,但似乎不起作用

Attempt 1 尝试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 尝试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 尝试3是尝试1和2的总和

  • code in RowSorterListener is designatet to returns the index correctly (from RowSorterListener s event) 指定RowSorterListener代码以正确返回索引(来自RowSorterListener的事件)

  • by default you never need to know ordering from JTable s view, all those events are models events , 默认情况下,您不需要从JTable的视图知道顺序,所有这些事件都是models events

  • add TableColumnModelListener in the case that you want to trace columnMoved , all events from sorting programatically are painted in JTable s view correctly 如果要跟踪columnMoved ,请添加TableColumnModelListener ,以编程方式排序的所有事件都将正确地绘制在JTable的视图中

  • 1st. 1号 attemtp without column reordering, attemtp无需重新排序列,

在此处输入图片说明

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. 2号 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 如果未初始化Swing Timer且所有事件均由用户手动进行,则尝试相同的正确输出

  • 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();
        });
    }
}

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: cf JTable的一般描述,引用相关部分:

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. 同样,使用RowSorter提供的排序和筛选功能时,底层的TableModel不需要知道如何进行排序,而是由RowSorter进行处理。 Coordinate conversions will be necessary when using the row based methods of JTable with the underlying TableModel . 当将JTable基于行的方法与基础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 . 所有基于JTables行的方法都是基于RowSorter ,不一定与基础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 . 例如,选择始终是根据JTable因此在使用RowSorter时,需要使用convertRowIndexToViewconvertRowIndexToModel进行转换。 [...] [...]

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: 为了处理这些转换,JTable中存在以下方法:

A small example to explain model vs view in a JTable in relation to your problem. 一个小示例,用于说明与您的问题有关的JTable中的模型vs视图。 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 (ie what you see on screen), your model is not changed (ie 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. 例如,数据模型中有三列A,B和C,然后将屏幕上的第二列拖到第一个位置,以便顺序在屏幕上变为B,A,C。 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. 该视图所做的是更改其映射以在第一位置显示列B,在第二位置显示A,在第三位置显示C。 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 . 所以映射是view:1->model:A, view:2->model:B, view:3->model:C并在拖动后变为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. JTable上的任何侦听器报告索引(行,列)时,它都会使用视图索引进行报告。 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 (ie 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. 如果您从表(即视图)接收到索引,并且想使用那些索引来查找模型中的值,则首先需要使用convertXXXToModel方法转换索引。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM