JTable,自定义标题渲染器和排序图标

9
在JTable头部设置自定义渲染器时,我能够获得预期的视觉效果(边框、字体、对齐等),但是我无法获取通常在对行进行排序时出现的外观和感觉的LaF排序图标。
以下是设置自定义头部渲染器的代码:
Enumeration<TableColumn> columns = getColumnModel().getColumns();
   while (columns.hasMoreElements())
   columns.nextElement().setHeaderRenderer(new XDeliveryTableHeaderRenderer());

这是自定义标题呈现器的摘录:
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;
     }
}

有什么提示吗?

1个回答

12

尝试委托给已安装的外观渲染器:

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()));

正如kleopatra警告的那样,这可能不是最稳定的解决方案,请参见此错误报告,我在生产中刚遇到。该问题的报告者建议使用自定义的Table/TableColumn子类,在TableColumn#getDefaultRenderer中更新代理渲染器。


不幸的是,那个方法并没有奏效。继承JLabel并实现TableCellRenderer导致了一个具有相同背景颜色和无边框的表头(因此需要显式设置边框)。你提出的解决方案(通过....setHeaderRenderer(new MyTableHeaderRenderer(new DefaultTableCellRenderer()))调用)创建了一个具有白色背景的表头,仍然没有排序图标——但排序机制仍然正常工作。 - S3lvatico
1
啊,抱歉,我忘了,你需要传递table.getTableHeader().getDefaultRenderer()而不是new DefaultTableCellRenderer()。 - Walter Laan
已经注意到,尝试过并测试过,感激不尽。它像魔法般地运行,谢谢 :) - S3lvatico
2
这基本上是正确的方法,只需小心:标题渲染器高度依赖于LAF,并且它们的实现差异很大。因此,a)该简单设置将无法在其生命周期内经受LAF更改,b)期望特定类型的_renderer_的LAF可能决定不正确地配置组件c)排序图标可以位于任何位置,例如在边框中或占用标签的图标。所以请预见问题 :-) - kleopatra
1
至于第三个警告,我在玩弄自定义标题渲染器时进行了检查,这些渲染器带有自己的图标,例如 if (label.getIcon() == null) label.setIcon(MY_STOCK_ICONS[columnIndex]);。我想象中,同时拥有自定义标题图标和LAF排序图标需要手动合并两个资源,但这(目前)超出了我的应用程序范围。谢谢kleo。 - S3lvatico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接