Java 想要通过 table header 以合理的方式实现多列排序
In Java want to implement multi column sorting vai the table header in a sensible way
在Java11(with swingx 1.6.6)中,我刚刚实现了如下多列排序:
- 单击第一列,按该列升序排序
- 点击第二列,按该列升序二次排序
- 依此类推,如果用户单击了他们已经单击的列,则排序顺序发生变化,即升序为降序
但我没有办法重新设置排序并查看其他一些应用程序,我认为我希望它以下列方式工作:
- 单击第一列,按该列升序排序
- 点击第二列,将排序重置为仅按该列升序进行的主要排序(与默认排序相同)
- 但是,cntl-click 在第二列上,然后按该列进行二次排序,依此类推,如果用户单击某个列,他们已经单击了排序顺序更改,即升序为降序
因此,无论何时有人点击或 cntl-click 导致调用 toggleSort()
,但我如何捕获用户已 cntl-clicked 而不是点击并知道如此可访问至 toggleSort()
供参考,修改后的toggleSort()
方法extends
org.jdesktop.swingx.sort.TableSortController
并且我修改了 swingx 代码以便我可以访问以前的私有方法 getFirstInCycle()
和 getNextInCycle()
)
public void toggleSortOrder(int column)
{
//Are we allowed to this sort column
if (this.isSortable(column))
{
SortOrder firstInCycle = this.getFirstInCycle();
//If all already a column in sort cycle
if (firstInCycle != null)
{
//Make a copy of existing keys
List<SortKey> keys = new ArrayList(this.getSortKeys());
//Look for any existing sort key for column user has clicked on
SortKey sortKey = SortUtils.getFirstSortKeyForColumn((List)keys, column);
//If its the first one
if (((List)keys).indexOf(sortKey) == 0)
{
//Swap the sort order of to next one, i.e ascending to descending
((List)keys).set(0, new SortKey(column, this.getNextInCycle(sortKey.getSortOrder())));
}
else
{
//Add new final sort key for this column
((List)keys).add(new SortKey(column, this.getFirstInCycle()));
}
//Trim the number of keys if we have to many
if (((List)keys).size() > this.getMaxSortKeys()) {
keys = ((List)keys).subList(0, this.getMaxSortKeys());
}
this.setSortKeys((List)keys);
}
}
}
太好了,我一直在深入研究代码,我认为您遇到了很多问题。鼠标点击的处理由 UI 委托(特别是 BaseTableHeaderUI
)执行,它直接调用 TableRowSorter#toggleSortOrder
。所以 table 和 table header 根本不涉及,所以没有注入点,您可以通过它来控制此工作流。
然后我考虑简单地向 JTableHeader
本身添加一个 MouseListener
。我最初担心的是这会与 TableHeaderUI
使用的现有 MouseListener
接口,但如果我们只想在 header 为 [=] 时删除 SortKey
30=]Control+Clicked,然后它“应该”没问题,但是,你会接到两次 toggleSortOrder
的电话
现在,我没有使用 SwingX,这只是纯 Swing,但这个概念应该可行。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import static javax.swing.SortOrder.ASCENDING;
import static javax.swing.SortOrder.DESCENDING;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTable table = new JTable();
DefaultTableModel model = new DefaultTableModel(
new Object[]{"abc", "def", "ghi", "jkl"},
0);
model.addRow(new Object[]{"A", "B", "C", "I"});
model.addRow(new Object[]{"B", "C", "D", "J"});
model.addRow(new Object[]{"C", "D", "E", "K"});
model.addRow(new Object[]{"D", "E", "F", "L"});
model.addRow(new Object[]{"E", "F", "G", "M"});
model.addRow(new Object[]{"F", "G", "H", "N"});
table.setModel(model);
// table.setTableHeader(new CustomTableHeader(table));
table.getTableHeader().setDefaultRenderer(new DefaultTableHeaderCellRenderer());
table.setRowSorter(new TableRowSorter<DefaultTableModel>(model));
JTableHeader header = table.getTableHeader();
header.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (!(e.getSource() instanceof JTableHeader)) {
return;
}
JTableHeader header = (JTableHeader) e.getSource();
JTable table = header.getTable();
RowSorter<? extends TableModel> rowSorter = table.getRowSorter();
if (rowSorter == null) {
return;
}
int column = header.columnAtPoint(e.getPoint());
if (column == -1) {
return;
}
List<? extends SortKey> sortKeys = rowSorter.getSortKeys();
List<SortKey> newSortKeys = new ArrayList<>(sortKeys);
Optional<? extends SortKey> firstMatch = sortKeys
.stream()
.filter(key -> key.getColumn() == column)
.findFirst();
if (e.isControlDown()) {
if (firstMatch.isPresent()) {
SortKey sortKey = firstMatch.get();
newSortKeys.remove(sortKey);
}
}
rowSorter.setSortKeys(newSortKeys);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, false, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
}
setIcon(getIcon(table, column));
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
switch (sortKey.getSortOrder()) {
case ASCENDING:
return UIManager.getIcon("Table.ascendingSortIcon");
case DESCENDING:
return UIManager.getIcon("Table.descendingSortIcon");
}
}
return null;
}
protected SortKey getSortKey(JTable table, int column) {
RowSorter rowSorter = table.getRowSorter();
if (rowSorter == null) {
return null;
}
List sortedColumns = rowSorter.getSortKeys();
if (sortedColumns.size() > 0) {
return (SortKey) sortedColumns.get(0);
}
return null;
}
}
}
我在这里看到的问题是弄清楚 JTable
开关如何成为排序键与何时通过 Control+[= 实际删除排序键之间的区别30=]点击事件...
这就是我将双手高高举起并在升序、降序和 none 之间切换排序顺序的地方,因此您只需点击一下即可,但是就是我
最好放弃 cntl-click 的想法,而是通过修改 org.jdesktop.swingx.sort,DefaultSortController
从
来恢复三阶段循环
private final static SortOrder[] DEFAULT_CYCLE
= new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING};
到
private final static SortOrder[] DEFAULT_CYCLE
= new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING,SortOrder.UNSORTED};
然后这是我自定义排序控制器中的 toggleSortOrder() 方法
/**
* If new sort key sort ascending as after other existing sort keys
* If existing sort key and ascending cycle change to descending
* If existing sort key and descending remove the sort key
* If already at MAX_SORT_COLUMNS the ignore
*
* @param column
*/
@Override
public void toggleSortOrder(int column)
{
//Are we allowed to this sort column
if (this.isSortable(column))
{
SortOrder firstInCycle = this.getFirstInCycle();
//If all already a column in sort cycle
if (firstInCycle != null)
{
//Make a copy of existing keys
List<SortKey> newKeys = new ArrayList(this.getSortKeys());
//Look for any existing sort key for column user has clicked on
SortKey sortKey = SortUtils.getFirstSortKeyForColumn(newKeys, column);
//Existing Key
if(sortKey!=null)
{
//Get next in cycle
SortOrder nextSortOrder = this.getNextInCycle(sortKey.getSortOrder());
//Swap to descending/ascending
if(nextSortOrder==SortOrder.DESCENDING || nextSortOrder==SortOrder.ASCENDING)
{
newKeys.set((newKeys).indexOf(sortKey), new SortKey(column, nextSortOrder));
}
//Remove from sort
else
{
newKeys.remove(sortKey);
}
}
//New Key
else
{
(newKeys).add(new SortKey(column, this.getFirstInCycle()));
}
//Trim the number of keys if we have too many
if ((newKeys).size() > this.getMaxSortKeys()) {
newKeys = ((List)newKeys).subList(0, this.getMaxSortKeys());
}
this.setSortKeys(newKeys);
}
}
}
在Java11(with swingx 1.6.6)中,我刚刚实现了如下多列排序:
- 单击第一列,按该列升序排序
- 点击第二列,按该列升序二次排序
- 依此类推,如果用户单击了他们已经单击的列,则排序顺序发生变化,即升序为降序
但我没有办法重新设置排序并查看其他一些应用程序,我认为我希望它以下列方式工作:
- 单击第一列,按该列升序排序
- 点击第二列,将排序重置为仅按该列升序进行的主要排序(与默认排序相同)
- 但是,cntl-click 在第二列上,然后按该列进行二次排序,依此类推,如果用户单击某个列,他们已经单击了排序顺序更改,即升序为降序
因此,无论何时有人点击或 cntl-click 导致调用 toggleSort()
,但我如何捕获用户已 cntl-clicked 而不是点击并知道如此可访问至 toggleSort()
供参考,修改后的toggleSort()
方法extends
org.jdesktop.swingx.sort.TableSortController
并且我修改了 swingx 代码以便我可以访问以前的私有方法 getFirstInCycle()
和 getNextInCycle()
)
public void toggleSortOrder(int column)
{
//Are we allowed to this sort column
if (this.isSortable(column))
{
SortOrder firstInCycle = this.getFirstInCycle();
//If all already a column in sort cycle
if (firstInCycle != null)
{
//Make a copy of existing keys
List<SortKey> keys = new ArrayList(this.getSortKeys());
//Look for any existing sort key for column user has clicked on
SortKey sortKey = SortUtils.getFirstSortKeyForColumn((List)keys, column);
//If its the first one
if (((List)keys).indexOf(sortKey) == 0)
{
//Swap the sort order of to next one, i.e ascending to descending
((List)keys).set(0, new SortKey(column, this.getNextInCycle(sortKey.getSortOrder())));
}
else
{
//Add new final sort key for this column
((List)keys).add(new SortKey(column, this.getFirstInCycle()));
}
//Trim the number of keys if we have to many
if (((List)keys).size() > this.getMaxSortKeys()) {
keys = ((List)keys).subList(0, this.getMaxSortKeys());
}
this.setSortKeys((List)keys);
}
}
}
太好了,我一直在深入研究代码,我认为您遇到了很多问题。鼠标点击的处理由 UI 委托(特别是 BaseTableHeaderUI
)执行,它直接调用 TableRowSorter#toggleSortOrder
。所以 table 和 table header 根本不涉及,所以没有注入点,您可以通过它来控制此工作流。
然后我考虑简单地向 JTableHeader
本身添加一个 MouseListener
。我最初担心的是这会与 TableHeaderUI
使用的现有 MouseListener
接口,但如果我们只想在 header 为 [=] 时删除 SortKey
30=]Control+Clicked,然后它“应该”没问题,但是,你会接到两次 toggleSortOrder
现在,我没有使用 SwingX,这只是纯 Swing,但这个概念应该可行。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import static javax.swing.SortOrder.ASCENDING;
import static javax.swing.SortOrder.DESCENDING;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTable table = new JTable();
DefaultTableModel model = new DefaultTableModel(
new Object[]{"abc", "def", "ghi", "jkl"},
0);
model.addRow(new Object[]{"A", "B", "C", "I"});
model.addRow(new Object[]{"B", "C", "D", "J"});
model.addRow(new Object[]{"C", "D", "E", "K"});
model.addRow(new Object[]{"D", "E", "F", "L"});
model.addRow(new Object[]{"E", "F", "G", "M"});
model.addRow(new Object[]{"F", "G", "H", "N"});
table.setModel(model);
// table.setTableHeader(new CustomTableHeader(table));
table.getTableHeader().setDefaultRenderer(new DefaultTableHeaderCellRenderer());
table.setRowSorter(new TableRowSorter<DefaultTableModel>(model));
JTableHeader header = table.getTableHeader();
header.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (!(e.getSource() instanceof JTableHeader)) {
return;
}
JTableHeader header = (JTableHeader) e.getSource();
JTable table = header.getTable();
RowSorter<? extends TableModel> rowSorter = table.getRowSorter();
if (rowSorter == null) {
return;
}
int column = header.columnAtPoint(e.getPoint());
if (column == -1) {
return;
}
List<? extends SortKey> sortKeys = rowSorter.getSortKeys();
List<SortKey> newSortKeys = new ArrayList<>(sortKeys);
Optional<? extends SortKey> firstMatch = sortKeys
.stream()
.filter(key -> key.getColumn() == column)
.findFirst();
if (e.isControlDown()) {
if (firstMatch.isPresent()) {
SortKey sortKey = firstMatch.get();
newSortKeys.remove(sortKey);
}
}
rowSorter.setSortKeys(newSortKeys);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, false, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
}
setIcon(getIcon(table, column));
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
protected Icon getIcon(JTable table, int column) {
SortKey sortKey = getSortKey(table, column);
if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
switch (sortKey.getSortOrder()) {
case ASCENDING:
return UIManager.getIcon("Table.ascendingSortIcon");
case DESCENDING:
return UIManager.getIcon("Table.descendingSortIcon");
}
}
return null;
}
protected SortKey getSortKey(JTable table, int column) {
RowSorter rowSorter = table.getRowSorter();
if (rowSorter == null) {
return null;
}
List sortedColumns = rowSorter.getSortKeys();
if (sortedColumns.size() > 0) {
return (SortKey) sortedColumns.get(0);
}
return null;
}
}
}
我在这里看到的问题是弄清楚 JTable
开关如何成为排序键与何时通过 Control+[= 实际删除排序键之间的区别30=]点击事件...
这就是我将双手高高举起并在升序、降序和 none 之间切换排序顺序的地方,因此您只需点击一下即可,但是就是我
最好放弃 cntl-click 的想法,而是通过修改 org.jdesktop.swingx.sort,DefaultSortController
从
private final static SortOrder[] DEFAULT_CYCLE
= new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING};
到
private final static SortOrder[] DEFAULT_CYCLE
= new SortOrder[] {SortOrder.ASCENDING, SortOrder.DESCENDING,SortOrder.UNSORTED};
然后这是我自定义排序控制器中的 toggleSortOrder() 方法
/**
* If new sort key sort ascending as after other existing sort keys
* If existing sort key and ascending cycle change to descending
* If existing sort key and descending remove the sort key
* If already at MAX_SORT_COLUMNS the ignore
*
* @param column
*/
@Override
public void toggleSortOrder(int column)
{
//Are we allowed to this sort column
if (this.isSortable(column))
{
SortOrder firstInCycle = this.getFirstInCycle();
//If all already a column in sort cycle
if (firstInCycle != null)
{
//Make a copy of existing keys
List<SortKey> newKeys = new ArrayList(this.getSortKeys());
//Look for any existing sort key for column user has clicked on
SortKey sortKey = SortUtils.getFirstSortKeyForColumn(newKeys, column);
//Existing Key
if(sortKey!=null)
{
//Get next in cycle
SortOrder nextSortOrder = this.getNextInCycle(sortKey.getSortOrder());
//Swap to descending/ascending
if(nextSortOrder==SortOrder.DESCENDING || nextSortOrder==SortOrder.ASCENDING)
{
newKeys.set((newKeys).indexOf(sortKey), new SortKey(column, nextSortOrder));
}
//Remove from sort
else
{
newKeys.remove(sortKey);
}
}
//New Key
else
{
(newKeys).add(new SortKey(column, this.getFirstInCycle()));
}
//Trim the number of keys if we have too many
if ((newKeys).size() > this.getMaxSortKeys()) {
newKeys = ((List)newKeys).subList(0, this.getMaxSortKeys());
}
this.setSortKeys(newKeys);
}
}
}