How to use Renderer for TableHeader

mKorbel picture mKorbel · Mar 24, 2012 · Viewed 9.6k times · Source

Even I read and test answers by @kleopatra

enter image description here

enter image description here

enter image description here

from SSCCE

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class SelectedTableHeader {

    private JFrame frame = new JFrame("Table Demo");
    private JTableHeader header;
    private Object selectedColumn = null;
    private String[] columnNames = {"String", "Integer", "Float", "Double", "Locale & Double", "Boolean"};
    private Object[][] data = {
        {"aaa", new Integer(12), new Float(12.15), new Double(100.05), new Double(12.05), true},
        {"bbb", new Integer(5), new Float(7.154), new Double(6.1555), new Double(417.55), false},
        {"CCC", new Integer(92), new Float(0.1135), new Double(3.1455), new Double(11.05), true},
        {"ddd", new Integer(12), new Float(31.15), new Double(10.05), new Double(23.05), true},
        {"eee", new Integer(5), new Float(5.154), new Double(16.1555), new Double(17.55), false},
        {"fff", new Integer(92), new Float(4.1135), new Double(31.1455), new Double(3.05), true}};
    private TableModel model = new DefaultTableModel(data, columnNames) {

        private static final long serialVersionUID = 1L;

        @Override
        public Class<?> getColumnClass(int column) {
            return getValueAt(0, column).getClass();
        }
    };
    private JTable table = new JTable(model);

    public SelectedTableHeader() {
        header = table.getTableHeader();
        header.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                JTableHeader h = (JTableHeader) e.getSource();
                int i = h.columnAtPoint(e.getPoint());
                Object o = h.getColumnModel().getColumn(i).getHeaderValue();
                if (i < 0) {
                    selectedColumn = null;
                    return;
                }
                selectedColumn = o;
                h.requestFocusInWindow();
            }
        });
        final TableCellRenderer hr = table.getTableHeader().getDefaultRenderer();
        header.setDefaultRenderer(new TableCellRenderer() {

            private JLabel lbl;

            @Override
            public Component getTableCellRendererComponent(
                    JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                if (selectedColumn == value) {
                    lbl = (JLabel) hr.getTableCellRendererComponent(table, value, true, true, row, column);
                    lbl.setBorder(BorderFactory.createCompoundBorder(lbl.getBorder(), BorderFactory.createLineBorder(Color.red, 1)));
                    lbl.setHorizontalAlignment(SwingConstants.LEFT);
                } else {
                    lbl = (JLabel) hr.getTableCellRendererComponent(table, value, false, false, row, column);
                    lbl.setBorder(BorderFactory.createCompoundBorder(lbl.getBorder(), BorderFactory.createEmptyBorder(0, 5, 0, 0)));
                    lbl.setHorizontalAlignment(SwingConstants.CENTER);
                }
                if (column == 0) {
                    lbl.setForeground(Color.red);
                } else {
                    lbl.setForeground(header.getForeground());
                }
                /*return (value == selectedColumn) ? hr.getTableCellRendererComponent(
                table, value, true, true, row, column) : hr.getTableCellRendererComponent(
                table, value, false, false, row, column);*/
                return lbl;
            }
        });
        table.setRowHeight(20);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scroll = new JScrollPane(table);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scroll);
        frame.pack();
        frame.setLocation(150, 150);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                SelectedTableHeader selectedTableHeader = new SelectedTableHeader();
            }
        });
    }
}

Answer

ryvantage picture ryvantage · Aug 11, 2013

In my experience, it's better to get the DefaultTableCellHeaderRenderer when you overwrite any JTable Renderer. So, instead of messing with the JLabel from the Renderer directly, you grab the Renderer with super(). So, your code should look like this:

header.setDefaultRenderer(new DefaultTableCellHeaderRenderer() {


    @Override
    public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        DefaultTableCellHeaderRenderer rendererComponent = (DefaultTableCellHeaderRenderer)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        if (selectedColumn == value) {
            rendererComponent.setBorder(BorderFactory.createCompoundBorder(rendererComponent.getBorder(), BorderFactory.createLineBorder(Color.red, 1)));
            rendererComponent.setHorizontalAlignment(SwingConstants.LEFT);
        } else {
            rendererComponent.setBorder(BorderFactory.createCompoundBorder(rendererComponent.getBorder(), BorderFactory.createEmptyBorder(0, 5, 0, 0)));
            rendererComponent.setHorizontalAlignment(SwingConstants.CENTER);
        }
        if (column == 0) {
            rendererComponent.setForeground(Color.red);
        } else {
            rendererComponent.setForeground(header.getForeground());
        }

        return rendererComponent;
    }
});

To try and answer your questions directly:

Question 1:

Q: How do I correctly use customer renderers to paint specific cells in a JTable?

A: Your current code is setting a Renderer on the JTableHeader. To add a Renderer on your table cells would be similar code to what's above, only you'd set it through the Column model:

table.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        // Set your code to render your component.

        return renderer;
    }

});

Note about this: JTables are column-based, which means that all the data in a certain column must be the same type (your SSCCE follows this convention). My favorite thing to do is to provide a custom Renderer for each type. For example, whenever I have a Date column, I use this renderer:

import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import org.joda.time.LocalDate;

/**
 *
 * @author Ryan
 */
public class DateCellRenderer extends DefaultTableCellRenderer {

    String pattern;
    public DateCellRenderer(String pattern){
        this.pattern = pattern;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        if (value != null && value instanceof LocalDate) {
            renderer.setText(((LocalDate)value).toString(pattern));
        } else
            throw new IllegalArgumentException("Only supported Object type is LocalDate.");

        return renderer;
    }
}

And I call this code with something similar:

table.getColumn("Date Entered").setCellRenderer(new DateCellRenderer("MMM dd, yyyy"));

Question 2:

Q: particular one table header color java swing

A: Umm.. Your SSCCE seems to have it figured out.

Question 3:

Q: about super.getTableCellRendererComponent(...) must be last code line before returns, I'm not able to write correct Renderer by those suggestion, for me works only this way

A: I'm not sure what you mean "must be last code line before returns." That is not the case, proven by the code snip I gave above

Question 4:

Q: JLabel is added for Borders, HorizontalAlignment and Foreground, especially Background caused me a few non_senses by using Component instead of JLabel, (not important here somehow)

A: Ok... the DefaultTableCellHeaderRenderer is sufficient for all of those, borders, alignment, foreground and background.