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 tablesFirst, 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 dataCreate and configure the JTable, which displays the data from the modelConnect the JTable to the model created in the first stepBut 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.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 barAUTO_RESIZE_NEXT_COLUMN: When a column is adjusted in the UI, adjust the next column the opposite wayAUTO_RESIZE_SUBSEQUENT_COLUMNS: During UI adjustment, change subsequent columns to preserve the total widthAUTO_RESIZE_LAST_COLUMN: During all resize operations, apply adjustments to the last column onlyAUTO_RESIZE_ALL_COLUMNS: During all resize operations, proportionately resize all columnsBy 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:Controlling JTable columnsYou can control the width of your columns, as well as make them nonresizable. This section shows you how.Wider columnsOften 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.Nonresizable columnsFor 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 selectionsWhy 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. Control headersAs 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 headerA feature-rich JTableIn 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. JavaSoftware Development