2

I want to recreate a table header looks using JLabel. The look and feel of the JLabel needs to be exactly like the JTableHeader would be, specified by the system.

This is what I have tried so far:

JLabel header = new JLabel("Title");
header.setOpaque(true);
header.setBackground(UIManager.getColor(new JTableHeader().getBackground()));
header.setBorder(UIManager.getBorder(new JTableHeader().getBorder()));

But, the UIManager returns null for the color and border.

Any ideas?

This is how I set the Look and Feel:

javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
Igor
  • 1,532
  • 4
  • 23
  • 44
  • Color and BorderUIResource, dependes of used L&F, could be important, for Metal doeasn't matter – mKorbel Nov 03 '12 at 22:42
  • @mKorbel Could you be more clear please? – Igor Nov 03 '12 at 22:53
  • [check whats returned](http://tips4java.wordpress.com/2008/10/09/uimanager-defaults/), for official Sun/Oracle L&Fs – mKorbel Nov 03 '12 at 22:54
  • @mKorbel This: `java.swing.plaf.BorderUIResource$CompoundBorderUIResouce@fadfa` for the border. Can I use it somehow? – Igor Nov 03 '12 at 23:01
  • @mKorbel in the app you suggested, if you choose "Windows" in the look and feel menu, and select the TableHeader item, you'll see components that looks like Windows classic. But, the table header they are shown in has a different look, that's the look I'm after. – Igor Nov 03 '12 at 23:09

4 Answers4

4

There are more issues involved then just getting the color and border of the table header. Each cell/column is rendered by a TableCellRenderer meaning that the values return by the UIManager may be ignored...

For example, the following renders the JTableHeader and applies border/background to a JLabel based on values returned by the UIManager under the Window's Look and Feel...

enter image description here

As you can see, there's quite a difference between them

How ever, if all you're interested in is display a "group header" of some kind over the top of another component on a scroll pane, you could simply add a JTableHeader to the scroll panes column view directly...

enter image description here

public class TestHeader {

    public static void main(String[] args) {
        new TestHeader();
    }

    public TestHeader() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                TableColumnModel model = new DefaultTableColumnModel();
                final TableColumn column = new TableColumn(0, 250);
                column.setHeaderValue("Test");
                model.addColumn(column);

                JTableHeader header = new JTableHeader();
                header.setColumnModel(model);

                final JTextArea textArea = new JTextArea();

                JScrollPane scrollPane = new JScrollPane(textArea);
                scrollPane.setColumnHeaderView(header);

                textArea.addComponentListener(new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent e) {
                        column.setWidth(textArea.getWidth());
                    }
                });

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(scrollPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

UPDATED

enter image description here

public class TestHeader {

    public static void main(String[] args) {
        new TestHeader();
    }

    public TestHeader() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                TableColumnModel model = new DefaultTableColumnModel();
                final TableColumn column = new TableColumn(0, 250);
                column.setHeaderValue("I don't see the problem");
                model.addColumn(column);

                final JTableHeader header = new JTableHeader();
                header.setColumnModel(model);

                DefaultTableModel tm = new DefaultTableModel(new Object[]{"A", "B", "C"}, 0);
                tm.addRow(new Object[]{"1", "2", "3", "4"});
                tm.addRow(new Object[]{"5", "6", "7", "8"});
                tm.addRow(new Object[]{"9", "10", "11", "12"});
                tm.addRow(new Object[]{"13", "14", "15", "16"});
                final JTable table = new JTable(tm);

                final JScrollPane scrollPane = new JScrollPane(table);
                /**
                 * For some reason, the header isn't being applied as soon as the
                 * table is added to the scroll pane, so we need to jump our next
                 * request to the end of the of event queue so that it will
                 * occur some time in the future
                 */
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        scrollPane.setColumnHeaderView(header);
                    }

                });

                table.addComponentListener(new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent e) {
                        column.setWidth(table.getWidth());
                    }

                });

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(scrollPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Works fine with `JTextArea`, but if you replace it with `JTable`, the header doesn't show at all. This would be useful if you wanted to replace the 2 headers in a 2 column table with only 1 header. – Igor Nov 05 '12 at 11:37
  • Is there any way to make this work with a `JTable` instead of the `JTextArea`? – Igor Nov 05 '12 at 13:23
  • What do you mean? JTable automatically configures the column header – MadProgrammer Nov 05 '12 at 19:46
  • Can the `JtableHeader` from the table be hidden and a new header added to the scrollpane? Or is it the same header we are talking about? I want to remove the headers from a 2 column table and put a single header over the two columns. – Igor Nov 05 '12 at 20:36
  • Sure, after you've added the table to scroll pane, you should be able to use the technique above to replace the eating header – MadProgrammer Nov 05 '12 at 20:41
  • Yes it does, there is a slight of hand involved though - check update to answer – MadProgrammer Nov 06 '12 at 18:42
  • Nice! I hope the thread won't be causing any problems later on. – Igor Nov 06 '12 at 22:12
  • 1
    It's not a thread. `invokeLater` places a `runnable` directly on the event queue, which is then executed, at some time in the future on the EDT ;) – MadProgrammer Nov 06 '12 at 22:37
  • Turns out runnable does cause a problem after all. At first, it's all OK, but when I replace the `JPanel` that contains the tables, from the right side of a `JSplitPane`, with another `JPanel` and then the replace this one with the first one again - _the old table headers come back._ Do you have any idea why this happens? – Igor Dec 05 '12 at 11:33
  • 1
    @igor each time a table is added to a scroll pane, it installs it headers. If this is major issue, you can extend from JTable and override [configureEnclosingScrollPane](http://docs.oracle.com/javase/7/docs/api/javax/swing/JTable.html#configureEnclosingScrollPane()), to will allow you the opportunity to replace the header with your own implementation – MadProgrammer Dec 05 '12 at 19:53
  • 1
    Thanks again. I think I've fixed it. I removed the runnable and just added this: `scrollPane.setColumnHeaderView(header); //Add the header to the ScrollPane` `table.setTableHeader(header); // Add this header to the table aswell` I don't know why but seems like it works. – Igor Dec 05 '12 at 20:10
  • BTW, you don't happen to be familiar with `Advanced RTFEditorKit` do you? http://java-sl.com/advanced_rtf_editor_kit.html – Igor Dec 05 '12 at 22:11
  • @Igor No, I'm not farmular with it, but it looks interesting – MadProgrammer Dec 05 '12 at 22:34
2

You need to set a look and feel for the application before trying:

header.setBackground(UIManager.getColor(new JTableHeader().getBackground()));
header.setBorder(UIManager.getBorder(new JTableHeader().getBorder()));

you should set a look and feel first like so:

  try {
    for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(info.getName())) {
            UIManager.setLookAndFeel(info.getClassName());
            break;
        }
    }
} catch (Exception e) {
    // If Nimbus is not available, you can set the GUI to another look and feel.
}

Here is an example:

enter image description here

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.JTableHeader;

public class Test {

    public Test() {
        initComponents();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    //set nimbus look and feel
                    for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                        if ("Nimbus".equals(info.getName())) {
                            UIManager.setLookAndFeel(info.getClassName());
                            break;
                        }
                    }
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                    e.printStackTrace();
                }

                new Test();
            }
        });
    }

    private void initComponents() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel header = new JLabel("Title");
        header.setBackground(UIManager.getColor(new JTableHeader().getBackground()));
        header.setBorder(UIManager.getBorder(new JTableHeader().getBorder()));

        frame.add(header);

        frame.pack();
        frame.setVisible(true);
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • I did set the look and feel: `setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());` – Igor Nov 03 '12 at 22:30
  • @Igor Your systems look and feel might not have a border color. I'd suggest using a Java L&F for this exact case as they all seem to work fine – David Kroukamp Nov 03 '12 at 22:36
  • Ofcourse they do - how would `JTableHeader` be shown if they didn't? – Igor Nov 03 '12 at 22:40
  • @Igor I cannot say for sure but what I mean is it does not have a color for JTable borders (this is no real clash to the system it just does not use a border than) I even left out the call in my example to the border coour and it still looked identical – David Kroukamp Nov 03 '12 at 22:42
  • Could you set your example to "Windows" look and feel and create a `JTable` and see if it would be the same? I think it might be a coincidence that they are the same in Nimbus. – Igor Nov 03 '12 at 22:48
2

Try taking the defaults from UIManager:

Color color = UIManager.getColor("TableHeader.background");
Border border = UIManager.getBorder("TableHeader.CellBorder");
tenorsax
  • 21,123
  • 9
  • 60
  • 107
0

I figured I'll create a JTable without any rows and place a JTextPane right underneath. And it works like charm.

JTextPane textPane = new JTextPane();
JPanel panel = new JPanel(new BorderLayout());

JTable table = new JTable(0, 1);
table.setPreferredScrollableViewportSize(new Dimension(600, 0));
JScrollPane js = new JScrollPane(table)

panel.add(js, BorderLayout.NORTH);
panel.add(new JScrollPane(textPane),BorderLayout.CENTER);
Igor
  • 1,532
  • 4
  • 23
  • 44
  • 1
    If you're just going to do that, just create `JTableHeader` with a single column and add it to the scroll pane's column header – MadProgrammer Nov 04 '12 at 01:46