问题
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 (fromRowSorterListener
s event)by default you never need to know ordering from
JTable
s view, all those events aremodels events
,add
TableColumnModelListener
in the case that you want to tracecolumnMoved
, all events from sorting programatically are painted inJTable
s view correctly1st. 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 underlyingTableModel
does not need to know how to do sorting, ratherRowSorter
will handle it. Coordinate conversions will be necessary when using the row based methods ofJTable
with the underlyingTableModel
. All ofJTables
row based methods are in terms of theRowSorter
, which is not necessarily the same as that of the underlyingTableModel
. For example, the selection is always in terms ofJTable
so that when usingRowSorter
you will need to convert usingconvertRowIndexToView
orconvertRowIndexToModel
. [...]
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