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 导致调用 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);
        }
    }
}