HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Now that Swing (a k a JFC) has been officially released, an abundance of electronic and print material is available to serve as both tutorial and reference. The problem with the books and articles I've seen thus far is that they devote the same amount of space to each Swing component. For example, simple controls such as JLabel and JButton have as much (relatively) written about them as is written about more complex controls like JTree and JTable. This article attempts to rectify some of the imbalance by providing an in-depth look at the Swing JTable.

JTable uses the same Model-View-Controller-type scenario that is evident throughout Swing. If you're unfamiliar with the MVC paradigm, you can get a good introduction in several places (such as the Swing Connection Web site, for instance), but the gist of MVC is that any changes you want to make to the data that's displayed are made in a model while the actual presentation of the data is left to a view that receives the data it's to display from the underlying model. Table 1 summarizes the differences between using a non-MVC table and a Swing JTable.

Table 1

With a non-MVC component, we actually tell the component itself to change the contents of one of its cells. With the Swing JTable, we update the underlying model for the table (which has been previously linked to this JTable) and then inform the JTable that its model has been updated.

JTable Classes and Interfaces
All of the JTable-specific functionality can be found in either the JTable class (which is within the javax.swing package) or one of the classes in the javax.swing.table package. Most of your programs should import both of these classes as follows:

import javax.swing.*; import javax.swing.table.*;

Note: The package-naming scheme within Swing changed for JFC version 1.1. Prior to this, the Swing packages started with com.sun.java.swing. For JFC versions 1.1 and after, the package names start with javax.swing.

When implementing a JTable in Swing, the TableModel, TableColumnModel, TableCellEditor and TableCellRenderer interfaces will be the most important. Swing also provides some default classes - AbstractTableModel, DefaultTableModel and DefaultColumnModel - that already implement these interfaces.

The main classes we'll use are the JTable, JTableHeader and TableColumn. Figure 1 shows how these classes and interfaces relate to one another.

Figure 1
Figure 1:

Default Table Model
Often, the best way to present a detailed concept such as a JTable is to start with an example (see Listing 1). Figure 2 shows a basic example of a JTable.

Figure 2
Figure 2:

In this example we're using the DefaultTableModel class (located in the javax.swing.table package) to implement our table model. The DefaultTableModel class extends the AbstractTableModel class, which implements the TableModel interface (as mentioned previously). This saves us work because we don't have to extend AbstractTableModel ourselves, but this ease of use comes at the expense of functionality, as we'll see in subsequent articles when we implement our own table models.

There are several constructors for the DefaultTableModel class, but I've chosen to use the constructor that requires both a two-dimensional array (for the actual data that should be displayed in the table) and a one-dimensional array for the column headers. Once we create the table model, we simply pass this table model to the constructor for our JTable and add the JTable to the frame. If you compile and run the code in Listing 1, you should see a JFrame similar to that in Figure 2.

Here are a few things to notice, however, about our simple table example:

  • No column headers are displayed.
  • The table information is clipped at the frame border and there are no scrollbars showing, so we have to resize the frame in order to see all the data.
  • All cells are editable - try typing over some of the existing data.
The first two observations we can correct by using another Swing control - a JScrollPane. The third we'll save for a subsequent article when we look at creating our own table model.

JScrollPane
Almost no table would be complete without the ability to scroll through the data items in the table. Fortunately, Swing makes it easy for us to add this functionality; we simply insert our JTable into a JScrollPane. With a slight modification to our original code, we create a constructor for a JScrollPane that takes the Component that should be displayed within the scroll pane as its argument. Listing 2 contains the modified code, and Figure 3 shows the resulting JTable.

Figure 3
Figure 3:

Note: There is a bug in version 1.0.1 of the Swing classes that won't show the column headers when you place a JTable inside a JScrollPane. This problem should be fixed in all versions of Swing 1.0.2 and above.

Placing the JTable inside a JScrollPane has automatically displayed our column headers and has added a vertical scrollbar. There is no horizontal scrollbar, however. Try resizing the frame; you'll see that the horizontal scrollbar won't appear regardless of how narrow you make the frame. The reason is that, by default, the autoResizeMode property of the JTable is set to AUTO_RESIZE_ALL_COLUMNS. Table 2 lists the valid settings for the autoResizeMode property along with a description of each.

Table 2

When you set the autoResizeMode property to AUTO_RESIZE_OFF, the JScrollPane containing your table may display a horizontal scrollbar rather than resizing the columns to fit the existing column widths. I say may because whether or not a scrollbar is actually displayed depends on the setting of the horizontalScrollBarPolicy property of the JScrollPane. If it is set to HORIZONTAL_SCROLLBAR_NEVER, for instance, no horizontal scrollbar will be displayed regardless of the size of the JTable the scroll pane contains. There is also a corresponding verticalScrollBarPolicy property for a JScrollPane. Table 3 contains a list of some of the relevant constants that can be used for the horizontalScrollBarPolicy and verticalScrollBarPolicy properties of the JScrollPane.

Table 3

Listing 3 contains a sample program that makes use of the autoResizeMode property of the JTable. It creates five different JFrames, each containing a JTable that has a different setting for its autoResizeMode property. Figure 4 shows the resulting frames. The code in Listing 4 takes the case where the autoResizeMode is set to AUTO_RESIZE_OFF and displays six frames, each with a different value for the scrollbar policy properties of the JScrollPane that contains the JTable. This code produces the output shown in Figure 5. Try adjusting the table widths and the column widths in each of these examples to see how the autoResizeMode, horizontalScrollBarPolicy and verticalScrollBarPolicy properties function.
Figure 4
Figure 4:
Figure 5
Figure 5:

In Listings 3 and 4 we use a different method to create the JTable than we have previously. Instead of first creating an instance of the DefaultTableModel class based on an array of data and an array of column headers, we simply pass the data and column headers directly to the JTable constructor. This essentially does the same thing, except it bypasses entirely the creation of a table model.

In the next part of this series we'll look at implementing the TableModel and TableColumnModel interfaces in order to achieve maximum flexibility in our JTable designs. Subsequent articles will reveal the techniques behind implementing editors and renderers for custom display and editing of your JTable data, and opportunities for enhancing the performance of the sometimes-laggard JTable component.

About the Author
Michael Hatmaker has worked for several years as a consultant and as an instructor at Loyola University. He is currently specializing in Java and CORBA development and is working on a large-scale trading system project at a major financial exchange. He can be reached at: [email protected]

	

Listing 1: Code for Simple JTable.
 
import com.sun.java.swing.*; 
import com.sun.java.swing.table.*; 

public class SimpleTableTest extends JFrame { 

   public SimpleTableTest() { 
      setLocation(100,100); 
      setSize(250,100); 

      String[][] data = {  {"eggs", "sandwich", "steak", "snickers"}, 
                           {"bacon", "pickles", "potato", "apple"}, 
                           {"syrup", "mayo", "corn", "banana"}, 
                           {"pancakes", "chips", "broccoli", "crunch bar"}, 
                           {"sausage", "pizza", "pie", "protein shake"}}; 
      String[] headers = {"Breakfast", "Lunch", "Dinner", "Snack"}; 
      DefaultTableModel model = new DefaultTableModel(data, headers); 
      JTable table = new JTable(model); 

      getContentPane().add(table); 

      setVisible(true); 
   } 

   public static void main(String[] args) { 
      SimpleTableTest simpleTableTest = new SimpleTableTest(); 
   } 
} 

Listing 2: JTable Inside JScrollPane.
 
import javax.swing.*; 
import javax.swing.table.*; 

public class SimpleTableTest extends JFrame { 

   public SimpleTableTest() { 
      setLocation(100,100); 
      setSize(250,100); 

      String[][] data = {  {"eggs", "sandwich", "steak", "snickers"}, 
                           {"bacon", "pickles", "potato", "apple"}, 
                           {"syrup", "mayo", "corn", "banana"}, 
                           {"pancakes", "chips", "broccoli", "crunch bar"}, 
                           {"sausage", "pizza", "pie", "protein shake"}}; 
      String[] headers = {"Breakfast", "Lunch", 
"Dinner", "Snack"}; 
      DefaultTableModel model = new DefaultTableModel(data, headers); 
      JTable table = new JTable(model); 
      JScrollPane scroll = new JScrollPane(table); 

      getContentPane().add(scroll); 

      setVisible(true); 
   } 

   public static void main(String[] args) { 
      SimpleTableTest simpleTableTest = new SimpleTableTest(); 
   } 
} 

Listing 3: Code to test the autoResizeMode property of the JTable.
 
import javax.swing.*; 

class TableColumnSize extends JFrame { 
    private static int offset = 50; 

    public TableColumnSize(int resizeMode, String title) { 
        // Dummy data for table. 
        String[][] tableData = { 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}}; 
        String[] headerData = {"Header 1", "Header 2", "Header 3", "Header 4"}; 

        // Set the frame's title and position. 
        setTitle(title); 
        offset += 50; 
        setLocation(offset,offset); 
        setSize(300,150); 

        // Create the JTable using the dummy data. 
        JTable table = new JTable(tableData, headerData); 

        // This is the important part of this example: Set the autoResizeMode 
        // of the JTable. 
        table.setAutoResizeMode(resizeMode); 

        // Create a scroll pane and insert the JTable into the scroll pane. 
        JScrollPane scroll = new JScrollPane(table); 

        getContentPane().add(scroll); 
    } 

    public static void main(String[] args) { 
        // Create 5 TableColumnSize frames - each demonstrating a 
        // different value of the autoResizeMode property. 
        (new TableColumnSize(JTable.AUTO_RESIZE_OFF,"AUTO_RESIZE_OFF")) 
.setVisible(true); 
        (new TableColumnSize(JTable.AUTO_RESIZE_ALL_COLUMNS, 
        "AUTO_RESIZE_ALL_COLUMNS")) 
.setVisible(true); 
        (new TableColumnSize(JTable.AUTO_RESIZE_LAST_COLUMN, 
        "AUTO_RESIZE_LAST_COLUMN")) 
.setVisible(true); 
        (new TableColumnSize(JTable.AUTO_RESIZE_NEXT_COLUMN, 
        "AUTO_RESIZE_NEXT_COLUMN")) 
.setVisible(true); 
        (new TableColumnSize(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS, 
        "AUTO_RESIZE_SUBSEQUENT_COLUMNS")) 
.setVisible(true); 
    } 
} 

Listing 4: Code to test the horizontalScrollBarPolicy and vertical 
Scroll BarPolicy of the JScrollPane.
 
import javax.swing.*; 

class ScrollBarTest extends JFrame { 
    private static int offset = 50; 

    public ScrollBarTest(int horizPolicy, int vertPolicy, String title) { 
        // Dummy data for table. 
        String[][] tableData = { 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}, 
            {"data 1", "data 2", "data 3", "data 4"}}; 
        String[] headerData = {"Header 1", "Header 2", "Header 3", 
            "Header 4"}; 

        // Set the frame's title and position. 
        setTitle(title); 
        offset += 50; 
        setLocation(offset,offset); 
        setSize(300,150); 

        // Create the JTable using the dummy data. 
        JTable table = new JTable(tableData, headerData); 

        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 

        // Create a scroll pane and insert the JTable into the scroll pane. 
        JScrollPane scroll = new JScrollPane(table); 

        // This is the important part of this example: Set the ScrollBar Policies 
        // of the JTable. 
        scroll.setHorizontalScrollBarPolicy(horizPolicy); 
        scroll.setVerticalScrollBarPolicy(vertPolicy); 

        getContentPane().add(scroll); 
    } 

    public static void main(String[] args) { 
        // Create 6 ScrollBarTest frames - each demonstrating a different 
        // value of the horizontalScrollBarPolicy and verticalScrollBarPolicy 
        // properties. 
       (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS,JScrollPane. 
VERTICAL_SCROLLBAR_NEVER,"HORIZONTAL_SCROLLBAR_ALWAYS")).setVisible(true); 
        (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED,JScrollPane. 
VERTICAL_SCROLLBAR_NEVER,"HORIZONTAL_SCROLLBAR_AS_NEEDED")).setVisible(true); 
        (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER,JScrollPane. 
VERTICAL_SCROLLBAR_NEVER,"HORIZONTAL_SCROLLBAR_NEVER")).setVisible(true); 
        (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER,JScrollPane. 
VERTICAL_SCROLLBAR_ALWAYS,"VERTICAL_SCROLLBAR_ALWAYS")).setVisible(true); 
        (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER,JScrollPane. 
VERTICAL_SCROLLBAR_AS_NEEDED,"VERTICAL_SCROLLBAR_AS_NEEDED")).setVisible(true); 
        (new ScrollBarTest(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER,JScrollPane. 
VERTICAL_SCROLLBAR_NEVER,"VERTICAL_SCROLLBAR_NEVER")).setVisible(true); 
    } 
} 

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.