【问题标题】:Displaying sort icons in JTable header without using the build in sort mechanism在 JTable 标题中显示排序图标而不使用内置排序机制
【发布时间】:2021-03-05 05:52:15
【问题描述】:

有人知道不使用内置排序功能在 JTable 标题中显示排序图标的好方法吗?

排序是由表模型(实际上是一个数据库)完成的,而不是由 JTable 本身完成的。这就是为什么图标的自动显示不起作用的原因。也许可以插入一个不执行任何操作但显示排序图标的虚拟 RowSorter?

我找到了更好的解决方案

我只是写了自己的RowSorter,这样排序没有任何作用,而是将排序请求重定向到模型。这样,排序顺序由外观本身显示。一些伪代码:

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.RowSorter;
import xyz.SortableTableModel;

public class MyRowSorter<M extends SortableTableModel> extends RowSorter<M> {

    private M tableModel;
    private List<? extends SortKey> sortKeys = new LinkedList<>();

    public MyRowSorter(M tableModel) {
        this.tableModel = tableModel;
    }

    @Override
    public M getModel() {
        return tableModel;
    }

    @Override
    public void toggleSortOrder(int column) {

        // redirecting sort request to model and modification of sortKeys

        List<? extends SortKey> newSortKeys = ...;
        setSortKeys(newSortKeys);
    }

    @Override
    public int convertRowIndexToModel(int index) {
        return index; // will always be the same
    }

    @Override
    public int convertRowIndexToView(int index) {
        return index; // will always be the same
    }

    @Override
    public void setSortKeys(List<? extends SortKey> keys) {
        if (keys == null) {
            sortKeys = Collections.EMPTY_LIST;
        } else {
            sortKeys = Collections.unmodifiableList(keys);
        }
        fireSortOrderChanged();
    }

    @Override
    public List<? extends SortKey> getSortKeys() {
        return sortKeys;
    }

    @Override
    public int getViewRowCount() {
        return tableModel.getRowCount();
    }

    @Override
    public int getModelRowCount() {
        return tableModel.getRowCount();
    }

    // no need for any implementation
    @Override public void modelStructureChanged() { }
    @Override public void allRowsChanged() { }
    @Override public void rowsInserted(int firstRow, int endRow) { }
    @Override public void rowsDeleted(int firstRow, int endRow) { }
    @Override public void rowsUpdated(int firstRow, int endRow) { }
    @Override public void rowsUpdated(int firstRow, int endRow, int column) { }

}

【问题讨论】:

  • 您可以尝试使用您的图标行为编写自定义 TableHeader Renderer。
  • 我已经试过了。但是这个解决方案的问题是,标题渲染器应该使用系统外观。自定义渲染器显然根本不使用外观和感觉(除了看起来像标签或其他东西)。
  • 查看我的答案,该答案提供了默认 L&F 和标题装饰的解决方案。
  • 一个自定义渲染器显然根本不使用观感——为什么你认为没有应用L&F

标签: java swing sorting jtable jtableheader


【解决方案1】:

在这种情况下,您可以尝试为JTableHeader 编写自定义TableCellRenderer

这是渲染器的简单示例:

private static class MyRenderer implements TableCellRenderer {

    private ImageIcon icon1;
    private ImageIcon icon2;
    private TableCellRenderer defaultRenderer;

    MyRenderer(JTable t){
        defaultRenderer = t.getTableHeader().getDefaultRenderer();
        icon1 = new ImageIcon(getClass().getResource("1.png"));
        icon2 = new ImageIcon(getClass().getResource("2.png"));
    }

    @Override
    public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
        Component c = defaultRenderer.getTableCellRendererComponent(  table, value, isSelected, hasFocus, row, col);
        if(col%2 == 0){
            ((JLabel)c).setIcon(icon1);
        } else {
            ((JLabel)c).setIcon(icon2);
        }
        return c;
    }
}

这里的icon1icon2 是您的排序图标。

您可以为您的JTableHeader 设置该渲染器,如下所示:

table.getTableHeader().setDefaultRenderer(new MyRenderer(table));

table - 是你的JTable

【讨论】:

  • 这是一个非常简单的解决方案,但它也有缺点: 1. 您不能再为列使用自定义图标。它被(用于)排序图标占据。 2. 标准的排序机制和你刚刚写的自定义的排序机制在视觉上是有区别的。见下图:
  • !Image of differences。默认行为在底部,您的自定义解决方案在顶部。
  • 我想,你可以尝试在更复杂的渲染器的帮助下自己管理图标的位置。
  • 这不起作用,因为我需要从原始渲染器传递 Component 对象,无法扩展。如果我不这样做,那么外观和感觉(例如动画)就很糟糕了。所以我想知道是否有可能添加一个假/虚拟的 RowSorter 或类似的东西,它只是忽略任何排序,但为外观设置正确的状态,这样我就不必实现错误自定义渲染器。
【解决方案2】:

排序是由表模型(实际上是一个数据库)完成的,而不是由 JTable 本身完成的。

查看DefaultRowSorter 课程。也许您使用setSortsOnUpdates(...)setSortKeys(...),以便排序图标与数据库中的排序相匹配。你可以试试:

  1. 创建一个空模型
  2. 设置排序键
  3. 使用setSortsOnUpdates(false)
  4. 使用 setDataVector() 更新模型(如果使用自定义模型,则使用其他等效方法)

请注意,此方法假定您已创建具有列名且没有数据的 TableModel,并将模型添加到 JTable。我认为您还需要使用:

table.setAutoCreateColumnsFromModel(false);

防止在将数据加载到模型时重新创建 TableColumnModel。

【讨论】:

  • 实际上我刚刚找到了一个非常相似的解决方案并将其直接发布在我的问题下。我刚刚实现了自己的 RowSorter,它或多或少什么都不做,除了存储 SortKey 并将排序请求重定向到 TableModel,它进行实际排序。
【解决方案3】:

当您希望您的代码与其他现有的 Swing 布局(我说的是 com.formdev .... flatlaf )一起工作时,解决方案很棘手。这些 L&F 会创建一个特殊的 Header 渲染器。

这是一个适用于市场上所有主要 L&F 的简单解决方案(tatoo、formdev、jgoodies)。诀窍是继承 DefaultTableCellHeaderRenderer 的子类,同时将表格外观和感觉当前标题渲染器作为参数传递。

        // this custom renderer will display the sorting icon for all afftected columns.
        class CustomTableHeaderRenderer extends DefaultTableCellHeaderRenderer  implements TableCellRenderer{
            final private Icon ascIcon   = UIManager.getIcon("Table.ascendingSortIcon");
            final private Icon descIcon = UIManager.getIcon("Table.descendingSortIcon");
            TableCellRenderer iTableCellRenderer = null;
            
            public CustomTableHeaderRenderer(TableCellRenderer tableCellRenderer)
            {
                iTableCellRenderer = tableCellRenderer;
            }           
            
            
            public Component getTableCellRendererComponent(JTable table,  Object value, boolean isSelected, boolean hasFocus, int row,  int column) {
                JLabel label = (JLabel) iTableCellRenderer.getTableCellRendererComponent( table,   value,  isSelected, hasFocus,  row,   column) ;
                List<? extends SortKey> sortKeys = table.getRowSorter().getSortKeys();
                label.setIcon(null);
                for (SortKey sortKey : sortKeys) {
                    if (sortKey.getColumn() == table.convertColumnIndexToModel(column)){
                        SortOrder o = sortKey.getSortOrder();
                        label.setIcon(o == SortOrder.ASCENDING ? ascIcon : descIcon);
                        break;
                    }
                }
                return label;
            }
        }

        yourTable.getTableHeader().setDefaultRenderer( new CustomTableHeaderRenderer(  yourTable.getTableHeader().getDefaultRenderer()      ));

【讨论】:

    猜你喜欢
    • 2011-04-17
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    相关资源
    最近更新 更多