JTable表头右对齐

31

基本上,我有一个包含右对齐单元格但左对齐标题的JTable,看起来非常糟糕。我想将这些列的标题右对齐,而不改变标题的“外观和感觉”。

谢谢

13个回答

47

这是另一种修改表格的TableCellRenderer的方法,适用于表格的JTableHeader。虽然在此使用情况下不是必需的,但它可以最小化对UI委托外观的影响。

典型用法:

JTable table = new JTable(…);
JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new HeaderRenderer(table));

自定义标题渲染器:

private static class HeaderRenderer implements TableCellRenderer {

    DefaultTableCellRenderer renderer;

    public HeaderRenderer(JTable table) {
        renderer = (DefaultTableCellRenderer)
            table.getTableHeader().getDefaultRenderer();
        renderer.setHorizontalAlignment(JLabel.CENTER);
    }

    @Override
    public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected,
        boolean hasFocus, int row, int col) {
        return renderer.getTableCellRendererComponent(
            table, value, isSelected, hasFocus, row, col);
    }
}

+1。这是正确的解决方案。昨天我有点累,脑子里想不出什么。设置一个新的渲染器并不是个好主意。 - Heisenbug
请参阅Darryl Burke的默认表头单元格渲染器 - trashgod
1
只是一个小问题,我认为这很小 H]eader header.setDefaultRenderer(new HeaderRenderer(table)); 顺便说一下,我也在寻找这个解决方案,它运行得很好。唯一需要更改的是将大写的 H 改为小写的 h - SüniÚr

44

试试这个:

((DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer())
    .setHorizontalAlignment(JLabel.RIGHT);

1
+1 JTableHeader 使用了一个 TableCellRenderer;在这个相关的示例中,setHorizontalAlignment() 能够正确地工作。 - trashgod
1
TableCellRenderer 没有 setHorizontalAlignment() 方法 :( - Drew Galbraith
2
不需要循环 - 标头只有一个默认渲染器 :-) - kleopatra
这是一种更简单的方法。 - sunil
只是提醒一下 - 如果使用自定义的头部类,可能会出现问题。 - Koenigsberg
显示剩余2条评论

6
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) your_jtable.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(0);

其中0代表中心。


7
建议使用 SwingConstants.CENTER 替代 0 - SingleShot

4
上述HeaderRenderer (由trashgod于2011年9月21日发布) 结合了Heisenbug的代码(2011年9月21日发布),仅在所有标题对齐的情况下才能正确工作。
如果您想以不同方式对齐不同的标题,则必须使用以下代码:
int[] alignments = new int[] { JLabel.LEFT, JLabel.RIGHT, JLabel.RIGHT };
for (int i = 0 ; i < jTable.getColumnCount(); i++){
  jTable.getTableHeader().getColumnModel().getColumn(i)
    .setHeaderRenderer(new HeaderRenderer(jTable, alignments[i]));
}

并且

private static class HeaderRenderer implements TableCellRenderer {
  DefaultTableCellRenderer renderer;
  int horAlignment;
  public HeaderRenderer(JTable table, int horizontalAlignment) {
    horAlignment = horizontalAlignment;
    renderer = (DefaultTableCellRenderer)table.getTableHeader()
        .getDefaultRenderer();
  }
  public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int col) {
    Component c = renderer.getTableCellRendererComponent(table, value,
      isSelected, hasFocus, row, col);
    JLabel label = (JLabel)c;
    label.setHorizontalAlignment(horAlignment);
    return label;
  }
}

那就是:
getTableCellRendererComponent中设置对齐方式,而不是在HeaderRenderer构造函数中设置。

3
关于包装默认表头的一件事情需要记住:不要保存对它们的引用。
如果您(或您的用户)在Windows 7上使用Windows Classic主题,并且您的应用程序设置默认系统LAF,则@trashgod发布的答案可能会给您带来问题。
它受到此错误的影响,该错误十年前就被发布了(真的)。如果您的表格正在显示并且您将Windows首选项中的主题从Aero主题切换到Windows Classic,则会出现一系列NPEs。您不应该保存渲染器的引用,因为它可能在某个时间点变得无效。包装应该以动态方式完成,正如错误报告的评论所建议的那样。我从那里获取了代码并创建了以下可运行示例:
import java.awt.*;
import java.lang.ref.WeakReference;
import javax.swing.*;
import javax.swing.table.*;

public class TestFrame extends JFrame {

    private static final boolean I_WANT_THE_BUG_TO_HAPPEN = true;

    public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, InstantiationException, UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                int res = JOptionPane.showConfirmDialog(null, "Do you want to use the XP L&F?", "laffo", JOptionPane.YES_NO_OPTION);
                if (res == JOptionPane.YES_OPTION) {
                    try {
                        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
                    } catch (Exception ex) {
                    }
                }
                new TestFrame().setVisible(true);
            }

        });

    }

    public class MyModel extends AbstractTableModel {

        public int getRowCount() {
            return 10;
        }

        public int getColumnCount() {
            return 10;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            return "" + rowIndex + " X " + columnIndex;
        }

    }

    public class MyJTable extends JTable {

        /**
         *
         */
        private static final long serialVersionUID = -233098459210523146L;

        public MyJTable(TableModel model) {
            super(model);
        }

        public void doSomething() {
            System.out.println("HEHE");
        }
    }

    public class MyAlternativeJTable extends JTable {

        private WeakReference<TableCellRenderer> wrappedHeaderRendererRef = null;
        private TableCellRenderer wrapperHeaderRenderer = null;

        public MyAlternativeJTable(TableModel model) {
            super(model);
        }

        private class MyAlternativeTableColumn extends TableColumn {

            MyAlternativeTableColumn(int modelIndex) {
                super(modelIndex);
            }

            @Override
            public TableCellRenderer getHeaderRenderer() {
                TableCellRenderer defaultHeaderRenderer
                        = MyAlternativeJTable.this.getTableHeader().getDefaultRenderer();
                if (wrappedHeaderRendererRef == null
                        || wrappedHeaderRendererRef.get() != defaultHeaderRenderer) {
                    wrappedHeaderRendererRef
                            = new WeakReference<TableCellRenderer>(defaultHeaderRenderer);
                    wrapperHeaderRenderer
                            = new DecoratedHeaderRenderer(defaultHeaderRenderer);
                }
                return wrapperHeaderRenderer;
            }
        }

        @Override
        public void createDefaultColumnsFromModel() {
            TableModel m = getModel();
            if (m != null) {
                // Remove any current columns
                TableColumnModel cm = getColumnModel();
                while (cm.getColumnCount() > 0) {
                    cm.removeColumn(cm.getColumn(0));
                }

                // Create new columns from the data model info
                for (int i = 0; i < m.getColumnCount(); i++) {
                    TableColumn newColumn = new MyAlternativeTableColumn(i);
                    addColumn(newColumn);
                }
            }
        }
    }

    private JPanel jContentPane = null;
    private JScrollPane jScrollPane = null;
    private JTable table1 = null;
    private JScrollPane jScrollPane1 = null;
    private JTable table2 = null;

    /**
     * This is the default constructor
     */
    public TestFrame() {
        super();
        initialize();
        int res = JOptionPane.showConfirmDialog(null, "Do you want to call updateUI() on the tables ?", "laffo", JOptionPane.YES_NO_OPTION);
        if (res == JOptionPane.YES_OPTION) {
            table2.updateUI();
            table1.updateUI();
        }
    }

    /**
     * This method initializes this
     *
     * @return void
     */
    private void initialize() {
        this.setSize(753, 658);
        this.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        this.setContentPane(getJContentPane());
        this.setTitle("JFrame");
    }

    /**
     * This method initializes jContentPane
     *
     * @return javax.swing.JPanel
     */
    private JPanel getJContentPane() {
        if (jContentPane == null) {
            jContentPane = new JPanel();
            jContentPane.setLayout(null);
            jContentPane.add(getJScrollPane(), null);
            jContentPane.add(getJScrollPane1(), null);
        }
        return jContentPane;
    }

    /**
     * This method initializes jScrollPane
     *
     * @return javax.swing.JScrollPane
     */
    private JScrollPane getJScrollPane() {
        if (jScrollPane == null) {
            jScrollPane = new JScrollPane();
            jScrollPane.setBounds(new java.awt.Rectangle(358, 0, 387, 618));
            jScrollPane.setViewportView(getTable1());
        }
        return jScrollPane;
    }

    /**
     * This method initializes table1
     *
     * @return javax.swing.JTable
     */
    private JTable getTable1() {
        if (table1 == null) {
            table1 = new JTable(new MyModel());
        }
        return table1;
    }

    /**
     * This method initializes jScrollPane1
     *
     * @return javax.swing.JScrollPane
     */
    private JScrollPane getJScrollPane1() {
        if (jScrollPane1 == null) {
            jScrollPane1 = new JScrollPane();
            jScrollPane1.setBounds(new java.awt.Rectangle(0, 0, 350, 618));
            jScrollPane1.setViewportView(getTable2());
        }
        return jScrollPane1;
    }

    /**
     * This method initializes table2
     *
     * @return javax.swing.JTable
     */
    private JTable getTable2() {
        if (table2 == null) {
            if (I_WANT_THE_BUG_TO_HAPPEN) {
                table2 = new MyJTable(new MyModel());
                JTableHeader header = table2.getTableHeader();
                TableCellRenderer render = new DecoratedHeaderRenderer(header.getDefaultRenderer());
                header.setDefaultRenderer(render);
            } else {
                table2 = new MyAlternativeJTable(new MyModel());
            }
        }
        return table2;
    }

    private class DecoratedHeaderRenderer implements TableCellRenderer {

        public DecoratedHeaderRenderer(TableCellRenderer render) {
            this.render = render;
        }
        private TableCellRenderer render;

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component c = render.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            return c;
        }

    }

}

只需运行示例并两次选择,然后观察其崩溃。然后将I_WANT_THE_BUG_TO_HAPPEN静态成员更改为false并重复。设置为true的情况基本上与此处最受欢迎的答案相同。此示例的最重要部分是扩展的JTableMyAlternativeJTable),它可以动态包装。
目前接受的答案是广泛使用的,但不建议使用。您可以在许多应用程序中重现它,包括基于Java的Netbeans 8.0.2,而它正在显示可排序表,例如窗口> IDE工具>通知,其中您也会得到NPE报告,具有讽刺意味。只需在Windows 7上从Aero切换到Windows Classic(通过右键单击桌面>个性化>更改计算机的视觉和声音)即可。
如果您使用Glazed Lists并调用ca.odell.glazedlists.swing.TableComparatorChooser.install,那么您也会受到影响。它会注入自己的自定义渲染器以进行排序箭头。
我偶然发现了这个问题,当时我试图找到一个解决这个问题的方法,我认为这两个问题是相关的。

干得好。希望我能多次投票支持这个回答。 - kmort

2
for (int i = 0 ; i < table.getColumnCount(); i++){

    DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
    renderer.setHorizontalAlignment(SwingConstants.RIGHT);
    table.getColumn(i).setHeaderRenderer(renderer);

}

1
这就是最初的代码,但不幸的是它会覆盖标题的外观。同时,您必须使用"DefaultTableCellRenderer"类作为渲染器对象,因为"TableCellRenderer"没有"setHorizontalAlignment"方法。 - Drew Galbraith
1
这个方法有点拙劣,但也许可以获取现有的渲染器并进行 instanceof 操作,然后将其转换为 DefaultTableCellRenderer,再设置对齐方式?或者也许有一种方法可以将新的 () 强制转换到适当的 L&F 中? - Kevin Reid
其实我错了。我不知道有什么更好的方法可以做到这一点。也许@Kevin Reid是对的。让我们等待,看看是否有任何Swing大师能够告诉我们更多关于这个的信息。 - Heisenbug
3
与其尝试装饰一个新的渲染器,将对齐方式设置在现有的渲染器上似乎更为可靠。(我不是专家,只是一个同行。) - trashgod
+1 针对这种方法进行批判性的审查。@mKorbel:我感觉我们_大家_都是 kleopatra 的学生。 :-) - trashgod
我同意Jeanette的观点,她对跨API的知识和清晰的批评使我们向前迈进了一大步,她的知识是无穷无尽的宝库。嗯,在我们周围还有一些Java和Swing API/Frameworks/L&F构建者,以及前Old.Sun员工(不确定...但我认为没有人与当前所有者签订合同,某些事情发生在Persons & JavaFX),但只有Jeanette是这里的日常贡献者。 - mKorbel

2
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) 
MSISDNTable.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(JLabel.RIGHT);

其中MSISDNTable是您的表格


1
DefaultTableCellRenderer defaultHeaderRenderer = (DefaultTableCellRenderer) getTableHeader().getDefaultRenderer();
defaultHeaderRenderer.setHorizontalAlignment(JLabel.CENTER);         
getTableHeader().setDefaultRenderer(defaultHeaderRenderer);

我已在JAVA8中进行了测试,运行正常。


不需要使用 getTableHeader().setDefaultRenderer(defaultHeaderRenderer); - hdvianna

0

试试这段代码,

JTableHeader jtableHeader = jtable.getTableHeader();
DefaultTableCellRenderer rend = (DefaultTableCellRenderer) jtable.getTableHeader().getDefaultRenderer();
rend.setHorizontalAlignment(JLabel.CENTER);
jtableHeader.setDefaultRenderer(rend);

嗯...与之前的答案相比没有什么新的东西,是吧;-) 顺便说一句:不需要再设置默认渲染器,它已经是默认渲染器了。 - kleopatra

0
我基于pvbemmelen62的解决方案创建了一个类,可以非常容易地使用,例如:
AlignHeaderRenderer.install(myTable, new int[] { SwingConstants.RIGHT,
                        SwingConstants.RIGHT, SwingConstants.LEFT });

或者

AlignHeaderRenderer.install(myTable, 0, SwingConstants.RIGHT);
AlignHeaderRenderer.install(myTable, 1, SwingConstants.RIGHT);

这是代码:

public class AlignHeaderRenderer implements TableCellRenderer {

private final TableCellRenderer renderer;
private final int alignment;

public static void install(final JTable table, final int[] alignments) {
    for (int i = 0; i < alignments.length; ++i)
        install(table, i, alignments[i]);
}

public static void install(final JTable table, final int row,
        final int alignment) {
    table.getTableHeader().getColumnModel().getColumn(row)
            .setHeaderRenderer(new AlignHeaderRenderer(table, alignment));
}

private AlignHeaderRenderer(final JTable table, final int alignment) {
    renderer = table.getTableHeader().getDefaultRenderer();
    this.alignment = alignment;
}

@Override
public Component getTableCellRendererComponent(final JTable table,
        final Object value, final boolean isSelected,
        final boolean hasFocus, final int row, final int col) {
    final Component c = renderer.getTableCellRendererComponent(table,
            value, isSelected, hasFocus, row, col);
    ((JLabel) c).setHorizontalAlignment(alignment);
    return c;
}

}

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