Java Tip 116: Set your table options -- at runtime!

Enhance the display and usability of JTable

Java Foundation Classes (JFC) offer a rich selection of components for building smart and interactive graphical user interfaces (GUIs). You can display tabular data using the javax.swing.JTable class. In this Java tip, we'll investigate how to solve some common JTable issues.

First, let's define our initial, basic JTable class, MyTable:

import javax.swing.table.*;
import javax.swing.*;
import java.awt.*;
public class MyTable extends JTable{
      //default constructor
      public MyTable(){
           super();
           }
      //constructor to create a table with given number of rows and columns
        public MyTable(int row, int col){
            super(row, col);
            }
     }

Pretty simple! Our initial MyTable implementation is just a stock JTable.

In the following sections, we'll work with various JTable display options -- such as scroll bars, column widths, selection, and other attributes. We'll extend MyTable and incorporate various methods that will support the display features we want to change. Each section adds a new method to the MyTable class, so in the end, we'll have a totally reusable JTable.

Scroll your tables

First, let's use our JTable to show some tabular data. I've created the TableColumnTest class to demonstrate JTable's capabilities:

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
/**Author Sonal Goyal, sonal_goyal@hotmail.com
*/
public class TableColumnTest{
    protected JFrame frame;
    protected JScrollPane scrollpane;
    protected MyTable table;
    public TableColumnTest(){
      //(1) Create the table model.
      DefaultTableModel dm = new DefaultTableModel();
      // Names for each of the columns.
      String[] columnNames = {
            "This is going to be a really long column header",
            "Column B", "Column C", "Column D", "Column E", "Column F",
            "Column G", "Column H", "Column I",  "Column J"
            };
      // The actual data values.
      Integer[][] data = new Integer[8][10];
      // Populate the data matrix.
      for (int row = 0; row < 8; row++){
            for (int col = 0; col < 10; ++col){
                  data[row][col] = new Integer(1000000);
            }
      }
      // Configure the model with the data and column headers.
      dm.setDataVector(data, columnNames);
      //(2) Create the table.
      table = new MyTable();
      //(3) Connect the model to the table.
      table.setModel(dm);
      //(4) Create a scroll pane for the table.
      scrollpane =  new JScrollPane(table);
      //(5) Make the table visible.
      frame =  new JFrame();
      frame.getContentPane().add(scrollpane);
      frame.setSize(200, 150);
      frame.setVisible(true);
  }
public static void main(String[] args){
       TableColumnTest test = new TableColumnTest();
}

The demonstration application is pretty straightforward. We construct a simple JTable by doing the following:

  • Create and configure the TableModel, which has information on rows, columns, column headers, and the actual data

  • Create and configure the JTable, which displays the data from the model

  • Connect the JTable to the model created in the first step

But there is a twist in this first code listing: a scroll pane is added in step 4. We display the constructed and configured table inside a JFrame; see Figure 1 for the scroll results.

Figure 1. Messy scroll

As shown in Figure 1, it's difficult to discern any column headers or table data. Although we've added a scroll bar, the horizontal scroll bar does not appear. A close look at the JTable class reveals why. The JTable class has an attribute for auto-resize mode, which determines if the table automatically resizes the column widths (to cover the table's entire width) and how it does that resizing. This can take any of the following values:

  • AUTO_RESIZE_OFF: Do not adjust column widths automatically; use a scroll bar
  • AUTO_RESIZE_NEXT_COLUMN: When a column is adjusted in the UI, adjust the next column the opposite way
  • AUTO_RESIZE_SUBSEQUENT_COLUMNS: During UI adjustment, change subsequent columns to preserve the total width
  • AUTO_RESIZE_LAST_COLUMN: During all resize operations, apply adjustments to the last column only
  • AUTO_RESIZE_ALL_COLUMNS: During all resize operations, proportionately resize all columns

By default, the JTable resizes the other columns to preserve overall appearance, which explains Figure 1. Hence, if we want to display the columns with a horizontal scroll bar, we add a method to MyTable and call it from the constructors:

    /**This method shows the horizontal scroll bar when required.
     * It's being called in the two constructors provided here.
     */
     public void showHorScroll(boolean show){
      if (show){
          setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      }else{
          setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
      }
     }

Figure 2 shows the display with the visible horizontal scroll bar:

Figure 2. Visible horizontal scroll bar

Controlling JTable columns

You can control the width of your columns, as well as make them nonresizable. This section shows you how.

Wider columns

Often you want a column wider or narrower than another. To change a column's width, you use the TableColumnModel:

      /**This method should be called to set the column
        *at pColumn index to a width of pWidth.
          */
      public void setColumnWidth(int pColumn, int pWidth){
            //Get the column model.
            TableColumnModel colModel = getColumnModel();
            //Get the column at index pColumn, and set its preferred width.
                colModel.getColumn(pColumn).setPreferredWidth(pWidth);
      }

You can also add a button and its action listener to the JFrame, so that clicking the button changes the table's width:

    JButton resizeButton = new JButton("Resize Third Column");
    setResizeButton.addActionListener(this);
    public void actionPerformed(ActionEvent e){
      //Check which button was clicked.
          if (e.getActionCommand().equals("Resize Third Column")){
                 System.out.println("Resize called - resizes third column
    to 300");
                 table.setColumnWidth(2, 300);
                 //Force GUI update.
                 table.invalidate();
                 frame.invalidate();
                 frame.validate();
                 frame.repaint();
            }

In this case, pColumn is the column index, and pWidth is the new width set. The before and after of clicking the Resize button are shown in Figures 3 and 4.

Figure 3. Before clicking the Resize button
Figure 4. After clicking the Resize button

Nonresizable columns

For general use, you can resize columns by dragging the headers. The following code removes the ability to resize based on pIsResize. If pIsResize is true, the column can be resized; otherwise, it cannot be resized:

    public void setResizable(int pColumn, boolean pIsResize){
      //Get the column model.
      TableColumnModel colModel = getColumnModel();
      //Set resizable or not.
      colModel.getColumn(pColumn).setResizable(pIsResize);
    }

In this case, pColumn is the index of the nonresizable column. Getting the column (getColumn(..)) and setting a simple property (setResizable(..)) is all you need to do.

Column selections

Why not select an entire column with the click of a button rather than a single cell? The JTable displays selected/deselected cells by calling a cell's isCellSelected(int row, int col) method. Overriding this method gives you the desired results, which are dependent on the boolean select, passed as a parameter to the setSelect() method. If true, the column will be selected; if false, it will not be selected. The key is to save the column as colSelect(), with a "select" flag indicating whether this column should be selected or deselected:

    int colSelect;
    boolean select;
    /** Sets the column at index col to selected or deselected
      * -based on the value of select.
     */
     public void setSelect(int col, boolean select){
      colSelect = col;
      this.select = select;
     }
    /**This method returns whether a particular cell is selected or not.
     */
    public boolean isCellSelected(int row, int column)
      throws IllegalArgumentException{
      //override the method for the column set in setSelect()
      if (colSelect == column){
          if (select)
            return true;
          else
            return false;
      } else {
          return super.isCellSelected(row, column);
      }
    }

Figure 5 displays the result where Column D has been selected.

Figure 5. Selected column

Control headers

As you might have noticed, the column header in the first column is longer than that column's width. We address this by resetting the column width:

/**Sets the header and column size as per the Header text
*/
public void setHeaderSize(int pColumn){
      //Get the column name of the given column.
      String value =  getColumnName(pColumn);
      //Calculate the width required for the column.
      FontMetrics metrics = getGraphics().getFontMetrics();
      int width = metrics.stringWidth(value) + 
(2*getColumnModel().getColumnMargin());
      //Set the width.
      setColumnWidth(pColumn, width);
}

With the above code executed, Figure 6 shows the result of the resized column header.

Figure 6. Visible column header

A feature-rich JTable

In this tip, we've tried various display options on a simple JTable, and changed those options after the table was displayed. In the process, we developed a table that offers richer user interaction capabilities. Explore the rest of JTable's features and find out which interesting ones you can create!

Sonal Goyal has been working with Java for the past three years. She is an India-based engineer and has worked extensively on design and implementation of object-oriented systems using Java IO, JFC, CORBA, i18n, and reflection. John D. Mitchell is the Java Tips coordinator for JavaWorld.

Learn more about this topic