在Java中想要以合理的方式通过表头实现多列排序

问题描述 投票:0回答:3

在 Java 11(带有 swingx 1.6.6)中,我刚刚实现了多列排序,如下所示:

  • 单击第一列,按该列升序排序
  • 单击第二列,按该列升序进行二次排序
  • 依此类推,如果用户单击某列,他们已经单击了排序顺序更改,即升序到降序

但是我没有办法重置排序并查看其他一些应用程序,我认为我希望它以以下方式工作:

  • 单击第一列,按该列升序排序
  • 单击第二列,仅按该列升序将排序重置为主要排序(与默认排序相同)
  • 但是,cntl-单击第二列,然后按该列进行二次排序,依此类推,如果用户单击一列,他们已经单击了排序顺序更改,即升序到降序

因此,每当有人单击或 cntl-clicks 都会导致调用

toggleSort()
,但我如何捕获用户已 cntl-clicked 而不是单击,并知道
toggleSort()

可以访问它

仅供参考,修改后的

toggleSort()
方法扩展了
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);
        }
    }
}
java swing jtable swingx
3个回答
1
投票

Sooo,我一直在研究代码,我认为你遇到了很多问题。 鼠标单击的处理由 UI 委托(特别是

BaseTableHeaderUI
)执行,它直接调用
TableRowSorter#toggleSortOrder
。 因此,表和表头根本不涉及,因此不存在可以控制此工作流程的注入点。

然后我考虑简单地在

MouseListener
本身添加一个
JTableHeader
。 我最初担心的是,这会与
MouseListener
使用的现有
TableHeaderUI
接口,但如果我们想要做的只是在标题为
Control
+Clicked 时删除 SortKey,那么它“应该” “没关系,但是,您会接到两个电话:
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+Clicked事件实际删除排序键之间的区别...

这就是我将双手举在空中,然后在升序、降序和无之间切换排序顺序,所以你只需要点击它即可,但这就是我


0
投票

决定更好地放弃 cntl-click 想法,而是通过修改

org.jdesktop.swingx.sort,DefaultSortController
from

来恢复三阶段循环
    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);
        }
    }
}

0
投票

我有解决您问题的正确方法。您必须禁用自动排序并指示排序器以正确的顺序对正确的列进行排序。完成您需要的一切:

  1. 标题单元格的渲染器:
public class RndDefaultTableHeaderCell extends JPanel implements TableCellRenderer {
    
        private static final long serialVersionUID = 1L;
    
        final private JLabel title = new JLabel();
        final private JLabel icon = new JLabel();
        final private JLabel ordinal = new JLabel();
        final private Icon ascIcon = UIManager.getIcon("Table.ascendingSortIcon");
        final private Icon descIcon = UIManager.getIcon("Table.descendingSortIcon");
    
        public RndDefaultTableHeaderCell() {
    
            super();
    
            setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            add(Box.createHorizontalGlue());
            add(title);
            add(icon);
            add(ordinal);
            add(Box.createHorizontalGlue());
            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
    
            Font f = ordinal.getFont();
            ordinal.setFont(f.deriveFont(f.getStyle() & ~Font.BOLD));
        }
    
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
    
            icon.setIcon(null);
            title.setText(value.toString());
            ordinal.setText(null);
    
            List<? extends SortKey> sortKeys = table.getRowSorter().getSortKeys();
            for (int i = 0; i < sortKeys.size(); i++) {
    
                SortKey sortKey = sortKeys.get(i);
    
                if (sortKey.getColumn() == table.convertColumnIndexToModel(column)) {
    
                    icon.setIcon(sortKey.getSortOrder() == SortOrder.ASCENDING ? ascIcon : descIcon);
    
                    if (sortKeys.size() > 1)
    
                        ordinal.setText("(" + (i + 1) + ")");
    
                    break;
                }
            }
    
            return this;
        }
    }

上面的渲染器将向上/向下箭头放置在每个有序列的列标题之后,以及在括号之间的有序列的序数之后。

  1. 您需要禁用所有列的自动排序功能,因为您必须指示要排序的内容,然后您需要管理标题列上的 mouseClick + ControlClick 以及简单的 mouseClick 以根据您的要求对列进行排序:
...
JTable table = new JTable(myModel);

TableRowSorter<MyModel> sorter = new TableRowSorter<MyModel>(myModel);

for (int i = 0; i < table.getColumnCount(); i++)
    sorter.setSortable(i, false);

table.setRowSorter(sorter);     

JTableHeader header = table.getTableHeader();

header.setDefaultRenderer(new RndDefaultTableHeaderCell());

header.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {

        int column = table.convertColumnIndexToModel(header.columnAtPoint(e.getPoint()));

        SortKey sortKeyAscending = new SortKey(column, SortOrder.ASCENDING);
        SortKey sortKeyDescending = new SortKey(column, SortOrder.DESCENDING);

        List<? extends SortKey> oldSortKeys = sorter.getSortKeys();
        List<SortKey> newSortKeys;

        if (e.isShiftDown()) {

            newSortKeys = new ArrayList<>(oldSortKeys);

            int index;
            if ((index = oldSortKeys.indexOf(sortKeyAscending)) != -1)

                newSortKeys.set(index, sortKeyDescending);
            else if ((index = oldSortKeys.indexOf(sortKeyDescending)) != -1)

                newSortKeys.set(index, sortKeyAscending);
            else

                newSortKeys.add(sortKeyAscending);
        } else {

            newSortKeys = new ArrayList<>();

            if (oldSortKeys.contains(sortKeyAscending))

                newSortKeys.add(sortKeyDescending);
            else

                newSortKeys.add(sortKeyAscending);
        }

        sorter.setSortKeys(newSortKeys);
    }
});

使用提供的代码,您将获得下图所示的结果: 结果示例 为了实现上述排序,我在 Prog 上单击了两次(第一次单击升序,第二次单击降序),然后在 Exchange 上单击按住 CONTROL 一次。

© www.soinside.com 2019 - 2024. All rights reserved.