行排序后禁用 JTableHeader 更新

Disable JTableHeader update after row sorting

我用 TableCellRenderer 自定义了一个 Jtable headers,这样过滤器图标就会出现,当用户点击过滤器图标时,一个 pop-up 过滤器像 excel's 出现(图 1)。但是,如果用户单击 header 的文本,则会应用行排序器。当应用行排序器时(图 2),它会覆盖自定义并且过滤器图标消失。有没有办法避免这种行为,同时将行排序器保持在 table header?

您可以通过使用自定义渲染器装饰 table header 的原始渲染器来实现此目的,这将添加所需的过滤图标。

例如,遵循一些代码来保留排序图标,同时在原始列旁边添加您自己的标签 header:

import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;

public class Main {
    
    private static class MyTableCellRenderer extends DefaultTableCellRenderer {
        
        private final TableCellRenderer originalRenderer;
        
        public MyTableCellRenderer(final TableCellRenderer originalRenderer) {
            this.originalRenderer = Objects.requireNonNull(originalRenderer);
        }

        @Override
        public Component getTableCellRendererComponent(final JTable table,
                                                       final Object value,
                                                       final boolean isSelected,
                                                       final boolean hasFocus,
                                                       final int row,
                                                       final int column) {
            final Component original = originalRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            
            if (row >= 0) //The header will have a row equal to -1.
                return original;
            
            final JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
            final JLabel filtering = new JLabel("Filter", JLabel.CENTER); //Your icon should go in this label...
            
            //Put some fancy background to make the positioning of the label clear:
            filtering.setOpaque(true);
            filtering.setBackground(Color.CYAN);
            
            //The default renderer comes with a border... Let's apply the border to the whole new container:
            if (original instanceof JComponent) {
                container.setBorder(((JComponent) original).getBorder());
                ((JComponent) original).setBorder(null);
            }
            
            container.add(filtering);
            container.add(original);
            
            return container;
        }
    }
    
    private static void createAndShowGUI() {
        final JTable table = new JTable(new Object[][] {
            new Object[]{"Data001", "Data002", "Data003"},
            new Object[]{"Data011", "Data012", "Data013"},
            new Object[]{"Data021", "Data022", "Data023"},
            new Object[]{"Data031", "Data032", "Data033"},
            new Object[]{"Data041", "Data042", "Data043"}
        }, new Object[] {"Column1", "Column2", "Column3"});
        
        table.setAutoCreateRowSorter(true);

        final JTableHeader header = table.getTableHeader();
        header.setDefaultRenderer(new MyTableCellRenderer(header.getDefaultRenderer()));
        
        final JFrame frame = new JFrame("Table header");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new JScrollPane(table));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::createAndShowGUI);
    }
}

您可以在创建的标签上使用您的图标,而不是单词 Filter(出现在每一列 header)。

参考文献:

  1. Source(参见部分:3. 保持排序图标)。
  2. How can I put a control in the JTableHeader of a JTable?