Data Management

Keep data looking clean with the Swing JTable class

Java Swing's JTable class offers several tools that make it easy to format large data collections into manageable tables. Find out how to turn this simple interface component into an indispensable development asset.


Swing’s popular JTable class provides a simple mechanism for displaying large sets of data. JTable has many facilities suitable for rendering and editing data, many of which can be customized to further enhance functionality. This article will provide a step-by-step introduction to the world of JTables.

Listing A contains the code for a simple example that demonstrates common JTable behavior. The user can change JTable’s layout, dragging and dropping columns or resizing them by dragging the line that separates them in the header.

The columns are stored in a String array:
String[] columnNames = {"Product","Number of Boxes","Price"};

The data is initialized and stored in a two-dimensional object array:
Object[][] data =
{
{"Apples", new Integer(5),"5.00"},
         {"Oranges", new Integer(3),"6.00"},
         {"Pears", new Integer(2),"4.00"},
         {"Grapes", new Integer(3),"2.00"},
};

The JTable is constructed using data and columnNames:
JTable table = new JTable(data, columnNames);

Viewing the JTable
The height and width of the JTable are set as follows:
table.setPreferredScrollableViewportSize(new Dimension(300, 80));

If one of the JTable columns or the JTable window itself is resized, the other columns might react by shrinking or enlarging to fill the new dimensions. It's possible to control this behavior using the setAutoResizeMode() method:
table.setAutoResizeMode(int mode);

Possible values of the mode integer field are:
AUTO_RESIZE_OFF
AUTO_RESIZE_NEXT_COLUMN
AUTO_RESIZE_SUBSEQUENT_COLUMNS
AUTO_RESIZE_LAST_COLUMN
AUTO_RESIZE_ALL_COLUMNS

Table defaults
The default color for the cells’ gridlines is Color.gray. To change the color of these gridlines, use:
table.setGridColor(Color.black);

You can change row heights with:
table.setRowHeight(int pixelHeight);

The height of the individual cells will be equal to the row height minus the height of the row margins.

By default, the selection background and foreground colors are determined by Swing's look-and-feel implementation. You can change selection colors using:
table.setSelectionBackground(Color.black); table.setSelectionForeground(Color.white);

You can also hide the cells’ gridlines, like this:
table.setShowHorizontalLines(false);
table.setShowVerticalLines(false);

Figure A depicts a JTable that has its horizontal gridlines hidden.

Figure A


Column widths
The JTable component has several classes and interfaces that represent the characteristics of the table. TableColumn keeps track of the column’s width and manages the resizing of columns, including maximum and minimum widths.

TableColumnModel manages collections of TableColumns as well as column selections. To set a particular column’s width, get a reference to the table’s column model. Then, get the desired TableColumn and call its setPreferredWidth() method:
TableColumncolumn = table.getColumnModel().getColumn(0);
column.setPreferredWidth(100);

When the user drags and drops columns, the column’s index is not changed. The getColumn(0) method will always return the correct column, no matter where it appears on the screen.

Headers
JTableHeader handles the display of JTable headers. You can subclass JTableHeader to get a customized layout. For example, if your application needs a header that spans multiple columns, simply subclass JTableHeader and incorporate it into your JTable.

You can specify whether the reordering of headers is allowed by getting a reference to the current JTable’s JTableHeader and calling its setReorderingAllowed() method:
table.getTableHeader().setReorderingAllowed(false);

Similarly, you can ensure that columns cannot be resized by dragging between the column headers. To do this, you use the setResizingAllowed() method:
table.getTableHeader().setResizingAllowed(false);

Selection modes
By default, when a user selects a cell in a JTable, the entire row is selected. There are several techniques for customizing the way a user can make selections. You can allow the user to select individual or multiple rows using the ListSelectionModel interface:
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

ListSelectionModel has the following fields:
  • SINGLE_SELECTION allows the selection of one row at a time.
  • SINGLE_INTERVAL_SELECTION allows the selection of a contiguous set of rows.
  • MULTIPLE_INTERVAL_SELECTION also allows selections of contiguous rows, but with extended functionality. It lets the user create multiple interval selections (noncontiguous rows) by using the [Ctrl] key.

The setCellSelectionEnabled() method lets users select both individual cells and whole rows simultaneously:
table.setCellSelectionEnabled(true);

If set to true, the setCellSelectionEnabled() method will also allow the selection of columns, as shown in Figure B, in conjunction with rows and individual cells.

Figure B


Editing cells
Our simple table allows the user to edit any of the cells in the table. Listing B shows a table that lets the programmer decide which cells can be edited. The first step is to create a custom TableModel:
class SimpleTableModel extends AbstractTableModel {}

The data is encapsulated in the TableModel, and when the JTable is initialized, the custom TableModel is passed as a parameter to the JTable constructor instead of the two-dimensional object array:
  SimpleTableModel myModel = new SimpleTableModel();
  JTable table = new JTable(myModel);

If you wanted to make the second and third columns editable and make the first column constant, you would override the TableModel’s isCellEditable() method:
public boolean isCellEditable(int row, int col){
if (col == 0) {return false;}
         else          {return true; }
}

Simple cell validation
To make sure that the user can type only integer values into, say, the second column (Number of Boxes), override the setValueAt() method and include the validation logic inside the new method. First, check to see whether a column is an integer and should thus contain only integer values:
if (data[0][col] instanceof Integer && !(value instanceof Integer))
{… } else { data[row][col] = value;}

Next, check that the value being inserted is an integer. If it isn't, the field should not be updated and an error message should be displayed:
try {
data[row][col] = new Integer(value.toString());
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(SimpleTable.this,
"Please enter only integer values.");
}

Background colors
Listing C contains the code for ColorTable.java, which demonstrates how to add color to a JTable. You can add background colors to a JTable by overriding its prepareRenderer() method:
JTable table = new JTable(data, columnNames){
   public Component prepareRenderer(TableCellRenderer r, int row, int col){}
};

Next, insert logic that decides which columns should have colors and what they should be:
if (col == 2 && !isCellSelected(row, col)){
     Color bg = new Color(200, 100, 30);
     c.setBackground(bg);
c.setForeground(Color.white);
}

Note that when you change the cell’s background color, you should also change the color of the text displayed in the cell to make the text more readable. Figure C displays a JTable with colors added to its first and third columns.

Figure C


Take control
Our examples are merely the basic foundation upon which JTables rest. Using these tools, you can quickly and easily rein in the possible formatting of tables generated by your Java applications and thereby keep your users from tripping over themselves during standard usage.

 

 

 

Editor's Picks

Free Newsletters, In your Inbox