Table zebra stripes are alternating subtle background stripes painted behind the table's rows in a graphical user interface (GUI). They improve the readability of long rows in wide tables, but the JTable class in Java's Swing doesn't support them. This tip shows how to extend JTable to add zebra background stripes.
Table of Contents
Code
The ZebraJTable class below extends JTable in Java's Swing package. It does three things:
- It overrides the
paintComponent()method to paint stripes throughout the table before the table draws its cells. - It overrides the
prepareRenderer()andprepareEditor()methods to set the background color of opaque cell components. This insures that their backgrounds match the stripes underneath. - It chooses zebra stripe colors automatically based upon the current background and selection colors.
This much will get you zebra stripes under the table's cells. But there is one table quirk to handle.
Unlike a JList or JTree, a JTable does not automatically grow to fill the full height of a JViewport in a JScrollPane. Instead, if the table is shorter than the viewport, the viewport's own background color is visible after the last row of the table. When using zebra stripes, this looks odd.
To fix this quirk, ZebraJTable overrides the table's getScrollableTracksViewportHeight() method to expand the table to fill the viewport. Expanding the table doesn't add more rows to the table, it just includes the empty space as part of the table's rectangular area. This lets us paint zebra strips in that space from the table's paintComponent() method.
The ZebraJTable class has been tested on all major operating systems, with all standard Java look and feel choices, and with default and application-specific table cell renderers and editors. It automatically updates its background stripe colors when the application changes colors, the look and feel changes colors, or when the user changes their OS-wide color theme (such as through the Windows Display control pane or the Mac or Linux Appearance preferences pane).
Here's the full code. It's long, but not complicated. In the sections after the code are usage examples and then a full explanation of each part of the approach.

/**
* A JTable that draws a zebra striped background.
*/
public class ZebraJTable
extends javax.swing.JTable
{
private java.awt.Color rowColors[] = new java.awt.Color[2];
private boolean drawStripes = false;
public ZebraJTable( )
{
}
public ZebraJTable( int numRows, int numColumns )
{
super( numRows, numColumns );
}
public ZebraJTable( Object[][] rowData, Object[] columnNames )
{
super( rowData, columnNames );
}
public ZebraJTable( javax.swing.table.TableModel dataModel )
{
super( dataModel );
}
public ZebraJTable( javax.swing.table.TableModel dataModel,
javax.swing.table.TableColumnModel columnModel )
{
super( dataModel, columnModel );
}
public ZebraJTable( javax.swing.table.TableModel dataModel,
javax.swing.table.TableColumnModel columnModel,
javax.swing.ListSelectionModel selectionModel )
{
super( dataModel, columnModel, selectionModel );
}
public ZebraJTable( java.util.Vector<?> rowData,
java.util.Vector<?> columnNames )
{
super( rowData, columnNames );
}
/** Add stripes between cells and behind non-opaque cells. */
public void paintComponent( java.awt.Graphics g )
{
if ( !(drawStripes = isOpaque( )) )
{
super.paintComponent( g );
return;
}
// Paint zebra background stripes
updateZebraColors( );
final java.awt.Insets insets = getInsets( );
final int w = getWidth( ) - insets.left - insets.right;
final int h = getHeight( ) - insets.top - insets.bottom;
final int x = insets.left;
int y = insets.top;
int rowHeight = 16; // A default for empty tables
final int nItems = getRowCount( );
for ( int i = 0; i < nItems; i++, y+=rowHeight )
{
rowHeight = getRowHeight( i );
g.setColor( rowColors[i&1] );
g.fillRect( x, y, w, rowHeight );
}
// Use last row height for remainder of table area
final int nRows = nItems + (insets.top + h - y) / rowHeight;
for ( int i = nItems; i < nRows; i++, y+=rowHeight )
{
g.setColor( rowColors[i&1] );
g.fillRect( x, y, w, rowHeight );
}
final int remainder = insets.top + h - y;
if ( remainder > 0 )
{
g.setColor( rowColors[nRows&1] );
g.fillRect( x, y, w, remainder );
}
// Paint component
setOpaque( false );
super.paintComponent( g );
setOpaque( true );
}
/** Add background stripes behind rendered cells. */
public java.awt.Component prepareRenderer(
javax.swing.table.TableCellRenderer renderer, int row, int col )
{
final java.awt.Component c = super.prepareRenderer( renderer, row, col );
if ( drawStripes && !isCellSelected( row, col ) )
c.setBackground( rowColors[row&1] );
return c;
}
/** Add background stripes behind edited cells. */
public java.awt.Component prepareEditor(
javax.swing.table.TableCellEditor editor, int row, int col )
{
final java.awt.Component c = super.prepareEditor( editor, row, col );
if ( drawStripes && !isCellSelected( row, col ) )
c.setBackground( rowColors[row&1] );
return c;
}
/** Force the table to fill the viewport's height. */
public boolean getScrollableTracksViewportHeight( )
{
final java.awt.Component p = getParent( );
if ( !(p instanceof javax.swing.JViewport) )
return false;
return ((javax.swing.JViewport)p).getHeight() > getPreferredSize().height;
}
/** Compute zebra background stripe colors. */
private void updateZebraColors( )
{
if ( (rowColors[0] = getBackground( )) == null )
{
rowColors[0] = rowColors[1] = java.awt.Color.white;
return;
}
final java.awt.Color sel = getSelectionBackground( );
if ( sel == null )
{
rowColors[1] = rowColors[0];
return;
}
final float[] bgHSB = java.awt.Color.RGBtoHSB(
rowColors[0].getRed( ), rowColors[0].getGreen( ),
rowColors[0].getBlue( ), null );
final float[] selHSB = java.awt.Color.RGBtoHSB(
sel.getRed( ), sel.getGreen( ), sel.getBlue( ), null );
rowColors[1] = java.awt.Color.getHSBColor(
(selHSB[1]==0.0||selHSB[2]==0.0) ? bgHSB[0] : selHSB[0],
0.1f * selHSB[1] + 0.9f * bgHSB[1],
bgHSB[2] + ((bgHSB[2]<0.5f) ? 0.05f : -0.05f) );
}
}
Examples
Construct a ZebraJTable and add it to a scroll pane:
ZebraJTable table = new ZebraJTable( items, names ); JScrollPane scrollList = new JScrollPane( table );
Construct a ZebraJTable, set the row height and margin, and add the table to a scroll pane:
ZebraJTable table = new ZebraJTable( items, names ); table.setRowHeight( 20 ); table.setRowMargin( 5 ); JScrollPane scrollList = new JScrollPane( table );
Construct a ZebraJTable, set the background and selection colors, and add the table to a scroll pane:
ZebraJTable table = new ZebraJTable( items, names ); table.setBackground( Color.darkGray ); table.setForeground( Color.white ); table.setSelectionBackground( Color.yellow ); table.setSelectionForeground( Color.black ); JScrollPane scrollList = new JScrollPane( table );
Explanation
A Java JTable object is always painted in two steps:
- If the table is opaque, the table's area is painted with a solid background color.
- For each table cell, a component is retrieved from the table's cell renderer or editor and painted.
To add zebra background stripes, the ZebraJTable class overrides both of these steps.
For step 1, overridding background painting lets the class prefill the entire table background with stripes before anything is drawn on top. These stripes are visible if the table is empty, if it is too short to fill the viewport of a JScrollPane, or when the table's cells are painted with non-opaque components.
For step 2, overriding access to the TableCellRenderer and TableCellEditor enables ZebraJTable to set the background colors of opaque cell components before they are rendered atop the background.
If you don't override both of these steps, you'll only get an odd-looking partial result. For instance, if you only do step 1, you'll get stripes everywhere except under opaque list items. And if you only do step 2, you'll get stripes under those opaque list items, but nowhere else.
Painting background stripes on the JTable
Normally, Java's JTable paintComponent() method calls the table's UI delegate to draw the background and the table's cell components. ZebraJTable overrides paintComponent() and paints alternating background stripes the width of the table and the height of each table row, including row margins. It then temporarily turns off opacity and call's the JTable's normal paintComponent() method to let the UI delegate do its job and paint the table cells. With opacity disabled, the UI delegate won't repaint the table background, leaving the stripes visible.
While most tables are drawn with uniform row heights, JTable and ZebraJTable support rows of varying height. Background stripes will have the same height as individual table rows, including row margins. The height of the last table row is used as the height for stripes that fill out the remainder of the viewport beyond the end of the table.
Since non-opaque components shouldn't fill their backgrounds, ZebraJTable doesn't add zebra stripes unless the table is opaque.
Painting background stripes on table cells
Normally, Java's JTable getCellRenderer() and getCellEditor() methods get the component to render or edit a cell's value by using the table's TableCellRenderer and TableCellEditor. JTable calls these methods from prepareRenderer() and prepareEditor() during use. ZebraJTable overrides these latter two methods, calls the parent class to get components, then sets their background colors. When the cell components are painted, their backgrounds match the table's zebra stripes! This works for any application-specific TableCellRenderer or TableCellEditor, showing zebra stripes behind any type of table cell component.
Expanding the table to the height of the scrolling viewport
By default, a JTable is only tall enough to accommodate its rows of data. When the table is shown within a JViewport of a JScrollPane, if the viewport is larger than the table, the viewport's own background color fills the area after the end of the table. This behavior differs from similar uses of JList and JTree and causes zebra background stripes to end awkwardly at the last row of the table instead of extending for the full viewport.
To fix this, ZebraJTable overrides the getScrollableTracksViewportHeight() method to return true when the table is shorter than the viewport. This causes viewport handling to expand the table to the full viewport height, which enables paintComponent() to add background stripes to the entire area.
Setting zebra background stripe colors
Naturally, you can use any colors you like for the zebra stripes. But usually these are subtle colors that match the style of the user's color theme for their OS, or the application's own color theme.
Unfortunately, Java does not have standard SystemColor objects or look and feel properties for zebra stripe colors. So, ZebraJTable makes a guess. It uses JTable's background color for even stripes, and computes a slightly different background color for odd stripes. Both colors are updated on each repaint so that the table automatically changes any time the user, application, or look and feel changes to the color theme.
You can test the color guesses by showing a ZebraJTable then changing your OS color theme. Use the Display control panel in Windows, and the Appearance preferences pane on the Mac or Linux
By the way, table zebra stripes tend to look better if you disable the table's cell grid. But ZebraJTable leaves this choice to you and your application.
Testing
This Java class has been tested and works well on the CDE/Motif, GTK+, Mac OS X, Metal, Windows, and Windows Classic look and feel choices on Linux, Mac, and Windows platforms for Java 5 and Java 6. The automatic stripe color algorithm works reasonably well for all standard color schemes on these platforms, but you may wish to tune it for your own application's needs.
Further reading
Related tips
- Java tip: How to add zebra background stripes to a JList. Using a simpler approach (but with no cell editors to contend with), this article adds background stripes to a Java Swing
JList. - Java tip: How to add zebra background stripes to a JTree. Using roughly the same approach, this article adds background stripes to a Java Swing
JTree.
Other articles and specifications
- Class JTable. The Java Swing API documentation for the
JTableclass in Java 6. - Java Web Start Persistence and JList Striping. This Java Tech Tip shows how to create a new cell renderer to add zebra background stripes for a
JList. The same approach works for cells in aJTable. However, the article is only a partial solution that doesn't override background painting, so it doesn't handle stripes behind transparent components, in empty tables, and beyond the end of short tables. - Printing JTables and From StringTokenizer to Scanner Tech Tips. This Java Tech Tip shows how to override
prepareRenderer()and set the background stripe color of rows. Unfortunately, like the article above, it doesn't overrideJTable'spaintComponent()method too so that background stripe colors are painted behind row margins and non-opaque cell components. It also doesn't handle extending tables to the full viewport height. - Alternating row colors in JTable and Making JTable look more at home on Mac OS and Gnome. These two Java articles are widely referenced and discuss painting background stripes for table rows and extending a table to the full viewport height. However, the background painting code doesn't honor component insets, doesn't handle non-uniform height rows, and doesn't paint stripes behind non-opaque cell components and in row margins. Its
prepareRenderer()override also inappropriately resets selection colors and the class doesn't handle editor backgrounds or disable zebra stripes when the table is not opaque. - Creating an iTunes-like JTable. This Java article builds upon the extended
JTableintroduced by the above article pair, adding features to adjust grid and selection highlighting styles to make tables look more like those in iTunes.

custom cell renderer
Nice work. Very well explained.
The JXTable from SwingX also supports this via the HighlighterFactory.
How do/would you keep the alternating background consistent when it's necessary to replace one of the column's renderers (i.e. get the background color from an adjacent column on the fly)?
thanks for all to this help
thanks for all to this help
speed issue
hello
it looks for me, that after applying your code to my tables (around 7 in whole app), whole application is running much more slower, because of re-painting the tables all the time (not only, when the content changed). is there any idea, how to improve the speed?
ZebraJTable speed
In a tutorial article like this, the more optimizations and features I add, the more complicated the code and the more confusing the tutorial. So, I simplified and focused only on how to create the stripes. The code is correct, but its not as efficient as it could be.
To improve performance,
paintComponent( )should callgetClipBounds( )on theGraphicscontext to get the current clipping area. The returned rectangle is the only part of the table that needs to be repainted. Using the clip bounds complicates the stripe for-loops but is otherwise not difficult. For small tables, the performance gain is minimal. But if you have a large table, and particularly one that scrolls within aJScrollPane, the performance difference is significant.You also could slightly improve performance by only recalculating the stripe colors when the table's background color or selection background color change. To do this, you'd have to override the
setBackground( )andsetSelectionBackground( )methods to note color changes from the application. You'll also have to register a listener with theUIManagerto detect color changes that result from changes to the Look and Feel. And you'll have to handle the special case where dynamicSystemColorobjects are used for the colors (very common). With all that, the performance gain is small compared to the code hassle and its probably better to just update the stripe color calculations on each repaint.Post new comment