Search code examples
javaswingjtablejtableheadertablerowsorter

Disable JTableHeader update after row sorting


I customized a Jtable headers with a TableCellRenderer so filter icon would appear, and as the user clicks on the filter icon, a pop-up filter like excel's appears (picture 1). However, if the user clicks on the text of the header, a row sorter is appliead. When the row sorter is applied (picture 2), it override the customization and filter icon disappears. Is there a way to avoid this behavior while keeping the row sorter on the table header?

enter image description here

enter image description here


Solution

  • You can do this by decorating the original renderer of the table header with a custom one which will add the desired icon of filtering.

    For example, follows some code to preserve the sort icons while adding your own label next to the original column 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);
        }
    }
    

    Instead of the word Filter (which appears on each column header) you can use your icons on the created label instead.

    References:

    1. Source (see the section: 3. Keeping sort icons).
    2. How can I put a control in the JTableHeader of a JTable?