Table Header Icons
Did you know that icon images can be displayed in a table component's column headers? Figure 8 shows an example.
Assigning icons to a JTable component's header columns makes them more colorful and informative.
To display an icon in a column header, first establish a renderer (a component that draws graphics) for that column header. This renderer is a class that implements the TableCellRenderer interface by providing a getTableCellRendererComponent method. In turn, this method returns a component capable of rendering the column header.
What component can be used to perform this rendering? How about a component based on the JLabel class? After all, JLabel is capable of rendering an icon image. The following code fragment creates a JLabel object that represents the rendering component:
JLabel l = new JLabel (new ImageIcon ("icon.gif"));
The above code fragment creates an ImageIcon object that loads an icon image from a file called icon.gif. This object is subsequently passed to a JLabel constructor during the creation of a JLabel object. After making a few other changes, this JLabel object can be returned from getTableCellRendererComponent.
To specify an icon for a particular column, call JTable's getColumn method to retrieve a reference to a TableColumn object that describes that column. Then call TableColumn's setHeaderRenderer method with an object created from the renderer class.
Listing 5 presents source code to a TableHeaderIcons application. This application was used to generate the GUI in Figure 8 and shows how easy it is to render icons in a table's header.
Listing 5 The TableHeaderIcons application source code
// TableHeaderIcons.java import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.table.*; class TableHeaderIcons extends JFrame { TableHeaderIcons (String title) { // Pass the title argument to the JFrame superclass so that the // contents of title appear in the JFrame's title bar. super (title); // Exit the application by calling System.exit when the user // either selects Close from the System menu or clicks the X // button. setDefaultCloseOperation (EXIT_ON_CLOSE); // Define the data model for the JTable. TableModel dataModel = new AbstractTableModel () { final static int NROWS = 20; final static int NCOLS = 5; Object [][] rowData = new Object [NROWS] []; // This instance initializer creates the data when an object // is created from this subclass of the AbstractTableModel // class. { for (int i = 0; i < NROWS; i++) rowData [i] = new Object [NCOLS]; int x = 0; for (int row = 0; row < NROWS; row++) for (int col = 0; col < NCOLS; col++) rowData [row] [col] = new Integer (2 * x++); } public int getColumnCount () { return NCOLS; } public int getRowCount () { return NROWS; } public Object getValueAt (int row, int col) { return rowData [row] [col]; } public boolean isCellEditable (int row, int col) { return false; } public void setValueAt (Object value, int row, int col) { rowData [row] [col] = value; fireTableCellUpdated (row, col); } }; // Create the JTable and assign it the predefined data model. JTable table = new JTable (dataModel); // Assign a custom renderer to column A, so that an icon can // be displayed in column A's header. table.getColumn ("A").setHeaderRenderer (new MyRenderer ()); // Create a JScrollPane and add the JTable to this container. JScrollPane sp = new JScrollPane (table); // Set the JTable's preferred "viewport" size for viewing // JTable data. table.setPreferredScrollableViewportSize (new Dimension (250, 180)); // Add the JScrollPane to the JFrame's content pane. getContentPane ().add (sp); // Set the size of the JFrame window. setSize (300, 250); // Show the JFrame window. setVisible (true); } public static void main (String [] args) { new TableHeaderIcons ("Table Header Icons"); } } class MyRenderer implements TableCellRenderer { // Create a JLabel for use as a renderer and pre-load this label // with an icon image. private JLabel l = new JLabel (new ImageIcon ("icon.gif")); public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // Extract the original header renderer for this column. TableCellRenderer tcr = table.getTableHeader () .getDefaultRenderer (); // Extract the component used to render the column header. Component c = tcr.getTableCellRendererComponent (table, value, isSelected, hasFocus, row, column); // Establish the font, foreground color, and border for the // JLabel so that the rendered header will look the same as the // other rendered headers. l.setFont (c.getFont ()); l.setForeground (c.getForeground ()); l.setBorder (((JComponent) c).getBorder ()); // Establish the column name. l.setText ((String) value); // Return the cached JLabel a the renderer for this column // header. return l; } }
TableHeaderIcons' getTableCellRendererComponent method doesn't create a separate copy of the JLabel object each time this method is called. Instead, it always returns the same cached JLabel object. Some renderers always return a new rendering component object when getTableCellRendererComponent is called. This object is thrown away once JTable finishes rendering. If a separate object is always created and then thrown away, this can have an impact on performanceespecially for a renderer that's responsible for rendering table cells. Therefore, consider caching a rendering component object (which is what TableHeaderIcons does) and only returning the cached object.