Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JTable with striped background

Tags:

java

swing

jtable

Using a different background color for odd and even rows is a commonly used trick to improve readability of large tables.

I want to use this effect in Swing's JTable. I started out by creating a custom table renderer, but this can only be used to paint actual cells, and I also want to add stripes to the "white" part of the table where there might be no cells. I can subclass JTable and override paintComponent(), but I would prefer an option where I can just change the table's rendering.

Is there a better way of doing this?

Edit: According to the answers so far this seems to be impossible without extending JTable. However, when I override JTable.paintComponent() it also only paints the area where there are rows. How can I paint the rest?

like image 246
Kees Kist Avatar asked Jul 06 '10 09:07

Kees Kist


3 Answers

Use getCellRect( getRowCount() - 1, 0, true ).y to get the top y-coordinate of the empty space, and then paint some Rectangles and (Grid-)Lines with paintComponent( Graphics g ).

jtable with empty space grid

To make it much easier for you, here's a long (but complete) solution ;-)

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class StripedEvenInWhitePartsTable extends JTable
{

  public StripedEvenInWhitePartsTable( String[][] data, String[] fields )
  {
    super( data, fields );
    setFillsViewportHeight( true ); //to show the empty space of the table 
  }


  @Override
  public void paintComponent( Graphics g )
  {
    super.paintComponent( g );

    paintEmptyRows( g );
  }


  public void paintEmptyRows( Graphics g )
  {
    Graphics newGraphics = g.create();
    newGraphics.setColor( UIManager.getColor( "Table.gridColor" ) );

    Rectangle rectOfLastRow = getCellRect( getRowCount() - 1, 0, true );
    int firstNonExistentRowY = rectOfLastRow.y; //the top Y-coordinate of the first empty tablerow

    if ( getVisibleRect().height > firstNonExistentRowY ) //only paint the grid if empty space is visible
    {
      //fill the rows alternating and paint the row-lines:
      int rowYToDraw = (firstNonExistentRowY - 1) + getRowHeight(); //minus 1 otherwise the first empty row is one pixel to high
      int actualRow = getRowCount() - 1; //to continue the stripes from the area with table-data

      while ( rowYToDraw < getHeight() )
      {
        if ( actualRow % 2 == 0 )
        {
          newGraphics.setColor( Color.ORANGE ); //change this to another color (Color.YELLOW, anyone?) to show that only the free space is painted
          newGraphics.fillRect( 0, rowYToDraw, getWidth(), getRowHeight() );
          newGraphics.setColor( UIManager.getColor( "Table.gridColor" ) );
        }

        newGraphics.drawLine( 0, rowYToDraw, getWidth(), rowYToDraw );

        rowYToDraw += getRowHeight();
        actualRow++;
      }


      //paint the column-lines:
      int x = 0;
      for ( int i = 0; i < getColumnCount(); i++ )
      {
        TableColumn column = getColumnModel().getColumn( i );
        x += column.getWidth(); //add the column width to the x-coordinate

        newGraphics.drawLine( x - 1, firstNonExistentRowY, x - 1, getHeight() );
      }

      newGraphics.dispose();

    } //if empty space is visible

  } //paintEmptyRows



  public Component prepareRenderer( TableCellRenderer renderer, int row, int column )
  {
    Component c = super.prepareRenderer( renderer, row, column );

    if ( !isRowSelected( row ) )
    {
      c.setBackground( row % 2 == 0 ? getBackground() : Color.ORANGE );
    }

    return c;
  }


  public static void main( String[] argv )
  {
    String data[][] = { { "A0", "B0", "C0" }, { "A1", "B1", "C1" }, { "A2", "B2", "C2" }, { "A3", "B3", "C3" }, { "A4", "B4", "C4" } };
    String fields[] = { "A", "B", "C" };

    JFrame frame = new JFrame( "a JTable with striped empty space" );
    StripedEvenInWhitePartsTable table = new StripedEvenInWhitePartsTable( data, fields );
    JScrollPane pane = new JScrollPane( table );

    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.add( pane );
    frame.setSize( 400, 300 );
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }

}

This example could be extended to:

  • fix the painted pseudogrid for variable RowHeights (I'm using the lowest height used in any row)
  • explain to the user why nothing happens if he clicks in the empty space to edit the cells (via tooltip)
  • add an extra row to the table model if the user clicks in the empty space (nooo! no Excel please!)
  • use the empty space to draw a reflection of the table (including all rendered data ( what for? ;-) )
like image 93
bobndrew Avatar answered Nov 03 '22 00:11

bobndrew


The JXtable provided by swingx allow you to implements such rendering Take a look at http://swinglabs.org/docs/components/JXTable/tutorial.jsp?step=3#RowHighlighting

like image 27
sly7_7 Avatar answered Nov 02 '22 23:11

sly7_7


You can also use a custom TableCellRenderer which will pick the color for you, with a part of code like this inside:

if (isSelected) //color remains the same while selected
{
    lFgColor = table.getSelectionForeground();
    lBgColor = table.getSelectionBackground();
}
else
{
    lFgColor = table.getForeground();
    if (row%2 != 0) //once out of two rows, change color
        lBgColor = table.getBackground();
    else
    {
        //New look and feels like nimbus declare this property, try to use it
        lBgColor = UIManager.getColor("Table.alternateRowColor"); 
        if (lBgColor == null) //If not, choose your own color
            lBgColor = UIManager.getColor("Table.light");
        }
    }
}

Edit: I missed the fact that you tried that already, and that you need to extend this color to the space without cells. To my knowledge, this is not possible with the current implementation of JTable or JXTable. The highlighters from JXTable are mostly sophisticated renderers, they still cater only to cells.

To extend the space, the only possibilities I see are:

  • draw it yourself, in your own component.
  • "hack" a way by adding a fake last column, with a custom JTableHeader which wouldn't display the last column header (and a renderer avoiding the grid for this last column). Also, the table resizing mode should be AUTO_RESIZE_LAST_COLUMN, in this case. This is a lot of conditions to make it work, and I'm not really sure it would work anyway.
like image 22
Gnoupi Avatar answered Nov 02 '22 22:11

Gnoupi