9

While setting a custom renderer on a JTable header I get the expected visual behavior (borders, font, alignment, ...) but I can't manage to get the LaF sorting icons that usually appear when the rows are sorted.

This is the code for setting the custom header renderer:

Enumeration<TableColumn> columns = getColumnModel().getColumns();
   while (columns.hasMoreElements())
   columns.nextElement().setHeaderRenderer(new XDeliveryTableHeaderRenderer());

This is an excerpt of the custom header renderer:

public class MyTableHeaderRenderer extends JLabel implements TableCellRenderer {
     private static final Font labelFont = new Font("Arial", Font.BOLD, 11);

     @Override
     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        setFont(labelFont);
        setHorizontalAlignment(SwingConstants.CENTER);
        setText(value.toString());
        setBorder(BorderFactory.createEtchedBorder());
        return this;
     }
}

Any hints?

S3lvatico
  • 262
  • 3
  • 9

1 Answers1

12

Try delegating to the L&F installed renderer:

public class MyTableHeaderRenderer implements TableCellRenderer {
 private static final Font labelFont = new Font("Arial", Font.BOLD, 11);

 private TableCellRenderer delegate;

 public MyTableHeaderRenderer(TableCellRenderer delegate) {
     this.delegate = delegate;
 } 

 @Override
 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

    Component c = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

    if(c instanceof JLabel) {
        JLabel label = (JLabel) c;
        label.setFont(labelFont);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setBorder(BorderFactory.createEtchedBorder());
    }
    return c;
 }
}

// Usage:
JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new MyTableHeaderRenderer(header.getDefaultRenderer()));

As kleopatra warned, this might not be the most stable solution, see this bug report which I just got in production. The reporter of that issue suggest using a custom Table/TableColumn sub classes which updating the delegate renderer in TableColumn#getDefaultRenderer.

Walter Laan
  • 2,956
  • 17
  • 15
  • Unfortunately that didn't do the trick. Extending JLabel and implementing the TableCellRenderer resulted in a header with the same background color and no border (hence the need to explicitly set the border). The solution you suggested (called via ....setHeaderRenderer(new MyTableHeaderRenderer(new DefaultTableCellRenderer())) ) created a header with a white background and still no sorting icons - yet with the sorting mechanics still working. – S3lvatico Oct 17 '11 at 14:15
  • 1
    Ah sorry forgot, instead of passing new DefaultTableCellRenderer() you need to pass table.getTableHeader().getDefaultRenderer(). – Walter Laan Oct 17 '11 at 15:02
  • Duly noted, tried, tested, appreciated. It worked like a charm, thanks :) – S3lvatico Oct 17 '11 at 15:19
  • 2
    that's basically the way to go, just a small beware: header renderers are highly LAF dependent and their implementation varies broadly. Consequently, a) that simple setup will not survive a change of LAF in its life-time, b) LAFs which expect a particular type of the _renderer_ might decide to not configure the component correctly c) sort icon may reside anywhere, f.i. in the border or occupying the label's icon. So expect problems ahead :-) – kleopatra Oct 18 '11 at 09:57
  • 1
    As for the third warning, I checked it while toying a bit with custom header renderers carrying their own icon, as in `if (label.getIcon() == null) label.setIcon(MY_STOCK_ICONS[columnIndex]);` . I imagine that having both custom header icon + LAF sorting icons would require manual merging of the two resources, yet this is (at the moment) outside the scope of my application. Thank you kleo. – S3lvatico Oct 18 '11 at 10:24